@svelterm/core 0.1.0 → 0.21.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/CHANGELOG.md +425 -0
- package/README.md +42 -29
- package/dist/src/cli/build.d.ts +13 -0
- package/dist/src/cli/build.js +119 -0
- package/dist/src/cli/bundle.d.ts +25 -0
- package/dist/src/cli/bundle.js +61 -0
- package/dist/src/cli/dev.d.ts +10 -0
- package/dist/src/cli/dev.js +152 -0
- package/dist/src/cli/devtools.d.ts +9 -0
- package/dist/src/cli/devtools.js +47 -0
- package/dist/src/cli/init.d.ts +8 -0
- package/dist/src/cli/init.js +153 -0
- package/dist/src/cli/main.d.ts +9 -0
- package/dist/src/cli/main.js +52 -0
- package/dist/src/cli/svt-bin.d.ts +2 -0
- package/dist/src/cli/svt-bin.js +6 -0
- package/dist/src/cli/svt.d.ts +14 -0
- package/dist/src/cli/svt.js +76 -0
- package/dist/src/components/text-buffer.js +8 -5
- package/dist/src/css/animation-runner.d.ts +15 -6
- package/dist/src/css/animation-runner.js +80 -29
- package/dist/src/css/animation.d.ts +12 -0
- package/dist/src/css/animation.js +21 -0
- package/dist/src/css/calc.js +4 -3
- package/dist/src/css/color.d.ts +19 -0
- package/dist/src/css/color.js +371 -62
- package/dist/src/css/compute.d.ts +30 -3
- package/dist/src/css/compute.js +272 -33
- package/dist/src/css/defaults.d.ts +1 -1
- package/dist/src/css/defaults.js +9 -0
- package/dist/src/css/easing.d.ts +9 -0
- package/dist/src/css/easing.js +95 -0
- package/dist/src/css/incremental.d.ts +1 -1
- package/dist/src/css/incremental.js +2 -2
- package/dist/src/css/interpolate.d.ts +13 -0
- package/dist/src/css/interpolate.js +41 -0
- package/dist/src/css/parser.js +59 -3
- package/dist/src/css/pseudo-elements.d.ts +9 -0
- package/dist/src/css/pseudo-elements.js +97 -0
- package/dist/src/css/selector.d.ts +17 -2
- package/dist/src/css/selector.js +128 -13
- package/dist/src/css/specificity.js +17 -6
- package/dist/src/css/values.d.ts +6 -1
- package/dist/src/css/values.js +13 -6
- package/dist/src/debug/context.d.ts +13 -0
- package/dist/src/debug/context.js +11 -0
- package/dist/src/debug/css.d.ts +12 -0
- package/dist/src/debug/css.js +28 -0
- package/dist/src/debug/dom.d.ts +17 -0
- package/dist/src/debug/dom.js +92 -0
- package/dist/src/devtools/DevTools.compiled.js +327 -0
- package/dist/src/devtools/DevTools.css.js +1 -0
- package/dist/src/devtools/client.d.ts +36 -0
- package/dist/src/devtools/client.js +76 -0
- package/dist/src/framelog.d.ts +54 -0
- package/dist/src/framelog.js +99 -0
- package/dist/src/headless.js +12 -4
- package/dist/src/index.d.ts +65 -3
- package/dist/src/index.js +609 -81
- package/dist/src/input/checkable.d.ts +8 -0
- package/dist/src/input/checkable.js +66 -0
- package/dist/src/input/details.d.ts +6 -0
- package/dist/src/input/details.js +34 -0
- package/dist/src/input/focus.d.ts +6 -0
- package/dist/src/input/focus.js +27 -9
- package/dist/src/input/keyboard.d.ts +2 -2
- package/dist/src/input/keyboard.js +32 -5
- package/dist/src/input/label.d.ts +8 -0
- package/dist/src/input/label.js +53 -0
- package/dist/src/input/modal.d.ts +9 -0
- package/dist/src/input/modal.js +28 -0
- package/dist/src/input/mouse.d.ts +2 -2
- package/dist/src/input/mouse.js +15 -2
- package/dist/src/input/select.d.ts +12 -0
- package/dist/src/input/select.js +63 -0
- package/dist/src/input/selection.d.ts +48 -0
- package/dist/src/input/selection.js +150 -0
- package/dist/src/layout/engine.d.ts +2 -0
- package/dist/src/layout/engine.js +1084 -142
- package/dist/src/layout/flex.js +4 -4
- package/dist/src/layout/size.js +3 -2
- package/dist/src/layout/text.d.ts +3 -2
- package/dist/src/layout/text.js +96 -17
- package/dist/src/layout/unicode.d.ts +20 -0
- package/dist/src/layout/unicode.js +121 -0
- package/dist/src/render/animation-clock.d.ts +51 -0
- package/dist/src/render/animation-clock.js +213 -0
- package/dist/src/render/ansi-text.d.ts +26 -0
- package/dist/src/render/ansi-text.js +131 -0
- package/dist/src/render/ansi.d.ts +18 -0
- package/dist/src/render/ansi.js +64 -19
- package/dist/src/render/border.js +166 -17
- package/dist/src/render/buffer.d.ts +1 -0
- package/dist/src/render/buffer.js +5 -2
- package/dist/src/render/color-depth.d.ts +8 -0
- package/dist/src/render/color-depth.js +59 -0
- package/dist/src/render/context.d.ts +1 -0
- package/dist/src/render/context.js +17 -21
- package/dist/src/render/cursor-emit.d.ts +18 -0
- package/dist/src/render/cursor-emit.js +50 -0
- package/dist/src/render/diff.d.ts +12 -0
- package/dist/src/render/diff.js +120 -0
- package/dist/src/render/generation.d.ts +9 -0
- package/dist/src/render/generation.js +14 -0
- package/dist/src/render/graphics-layer.d.ts +27 -0
- package/dist/src/render/graphics-layer.js +86 -0
- package/dist/src/render/image.d.ts +27 -0
- package/dist/src/render/image.js +113 -0
- package/dist/src/render/incremental-paint.d.ts +7 -3
- package/dist/src/render/incremental-paint.js +52 -79
- package/dist/src/render/inline.d.ts +59 -0
- package/dist/src/render/inline.js +219 -0
- package/dist/src/render/kitty-graphics.d.ts +24 -0
- package/dist/src/render/kitty-graphics.js +58 -0
- package/dist/src/render/paint-text.js +68 -22
- package/dist/src/render/paint.d.ts +8 -1
- package/dist/src/render/paint.js +328 -30
- package/dist/src/render/png.d.ts +13 -0
- package/dist/src/render/png.js +145 -0
- package/dist/src/render/scrollbar.d.ts +8 -2
- package/dist/src/render/scrollbar.js +71 -14
- package/dist/src/render/snapshot.js +3 -1
- package/dist/src/renderer/default.d.ts +7 -0
- package/dist/src/renderer/default.js +11 -0
- package/dist/src/renderer/index.d.ts +8 -2
- package/dist/src/renderer/index.js +4 -2
- package/dist/src/renderer/node.d.ts +109 -0
- package/dist/src/renderer/node.js +165 -1
- package/dist/src/terminal/capabilities.d.ts +33 -0
- package/dist/src/terminal/capabilities.js +66 -0
- package/dist/src/terminal/clipboard.d.ts +9 -0
- package/dist/src/terminal/clipboard.js +39 -0
- package/dist/src/terminal/io.d.ts +82 -0
- package/dist/src/terminal/io.js +155 -0
- package/dist/src/terminal/screen.d.ts +3 -10
- package/dist/src/terminal/screen.js +5 -28
- package/dist/src/terminal/stdin-router.d.ts +8 -5
- package/dist/src/terminal/stdin-router.js +22 -11
- package/dist/src/utils/node-map.d.ts +24 -0
- package/dist/src/utils/node-map.js +75 -0
- package/dist/src/vite/config.d.ts +62 -0
- package/dist/src/vite/config.js +191 -0
- package/docs/compatibility.md +67 -0
- package/docs/debug/devtools.md +40 -0
- package/docs/debug/svt.md +50 -0
- package/docs/distribution.md +106 -0
- package/docs/elements.md +120 -0
- package/docs/getting-started.md +177 -0
- package/docs/guide/css.md +187 -0
- package/docs/guide/input.md +143 -0
- package/docs/guide/layout.md +171 -0
- package/docs/guide/theming.md +94 -0
- package/docs/how-it-works.md +115 -0
- package/docs/inline-mode.md +77 -0
- package/docs/layout.md +106 -0
- package/docs/motion.md +91 -0
- package/docs/reference/README.md +65 -0
- package/docs/reference/css/properties/border-corner.md +82 -0
- package/docs/reference/css/properties/border-style.md +168 -0
- package/docs/reference.md +226 -0
- package/docs/selectors.md +80 -0
- package/docs/terminal-css.md +149 -0
- package/docs/terminals.md +83 -0
- package/package.json +28 -7
package/dist/src/css/color.js
CHANGED
|
@@ -1,79 +1,301 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
/**
|
|
2
|
+
* CSS Color Level 4 parser and resolver.
|
|
3
|
+
*
|
|
4
|
+
* Supports: hex (#rgb, #rrggbb, #rrggbbaa), rgb(), hsl(), hwb(),
|
|
5
|
+
* lab(), lch(), oklab(), oklch(), named colours, transparent,
|
|
6
|
+
* light-dark(a, b) (resolved against the active colorScheme).
|
|
7
|
+
* Both legacy comma syntax and modern space + / alpha syntax.
|
|
8
|
+
*/
|
|
9
|
+
// --- Public API ---
|
|
10
|
+
/**
|
|
11
|
+
* Expand `light-dark(a, b)` to whichever side matches the active scheme.
|
|
12
|
+
* Recursive on the chosen side so `light-dark(light-dark(...), x)` works.
|
|
13
|
+
* Returns the input unchanged when no light-dark() call is present.
|
|
14
|
+
*/
|
|
15
|
+
export function expandLightDark(value, scheme = 'dark') {
|
|
16
|
+
if (!value.includes('light-dark('))
|
|
17
|
+
return value;
|
|
18
|
+
const start = value.indexOf('light-dark(');
|
|
19
|
+
const argsStart = start + 'light-dark('.length;
|
|
20
|
+
// Find the matching closing paren (track nesting for nested colour funcs).
|
|
21
|
+
let depth = 1;
|
|
22
|
+
let i = argsStart;
|
|
23
|
+
while (i < value.length && depth > 0) {
|
|
24
|
+
const ch = value[i];
|
|
25
|
+
if (ch === '(')
|
|
26
|
+
depth++;
|
|
27
|
+
else if (ch === ')')
|
|
28
|
+
depth--;
|
|
29
|
+
if (depth === 0)
|
|
30
|
+
break;
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
if (depth !== 0)
|
|
34
|
+
return value; // unbalanced — give up, let resolveColor fail loudly
|
|
35
|
+
const args = value.slice(argsStart, i);
|
|
36
|
+
// Split on the comma at depth 0 (commas inside nested parens aren't separators).
|
|
37
|
+
let split = -1;
|
|
38
|
+
let d = 0;
|
|
39
|
+
for (let j = 0; j < args.length; j++) {
|
|
40
|
+
const ch = args[j];
|
|
41
|
+
if (ch === '(')
|
|
42
|
+
d++;
|
|
43
|
+
else if (ch === ')')
|
|
44
|
+
d--;
|
|
45
|
+
else if (ch === ',' && d === 0) {
|
|
46
|
+
split = j;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (split < 0)
|
|
51
|
+
return value;
|
|
52
|
+
const lightArg = args.slice(0, split).trim();
|
|
53
|
+
const darkArg = args.slice(split + 1).trim();
|
|
54
|
+
const picked = scheme === 'light' ? lightArg : darkArg;
|
|
55
|
+
const before = value.slice(0, start);
|
|
56
|
+
const after = value.slice(i + 1);
|
|
57
|
+
// Recurse so additional light-dark()s elsewhere in the string are also expanded.
|
|
58
|
+
return expandLightDark(before + picked + after, scheme);
|
|
59
|
+
}
|
|
9
60
|
export function resolveColor(value) {
|
|
10
61
|
const lower = value.toLowerCase().trim();
|
|
11
|
-
// ANSI named colors (exact match, highest priority)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
//
|
|
62
|
+
// ANSI named colors (exact match, highest priority for terminal rendering)
|
|
63
|
+
if (lower in ANSI_COLORS)
|
|
64
|
+
return ANSI_COLORS[lower];
|
|
65
|
+
// transparent — terminal output has no alpha layer, so the closest
|
|
66
|
+
// semantic match is "no colour set": let the parent's bg show through.
|
|
67
|
+
if (lower === 'transparent')
|
|
68
|
+
return 'default';
|
|
69
|
+
// Hex colors stay truecolor, even when they equal a basic ANSI colour:
|
|
70
|
+
// keywords are themeable (mapped to SGR names the terminal palette can
|
|
71
|
+
// restyle), an explicit hex is exact.
|
|
16
72
|
if (lower.startsWith('#')) {
|
|
17
|
-
|
|
18
|
-
return hexToNearestAnsi(expanded) ?? expanded;
|
|
19
|
-
}
|
|
20
|
-
// rgb() / rgba()
|
|
21
|
-
if (lower.startsWith('rgb')) {
|
|
22
|
-
return parseRgbFunc(lower);
|
|
73
|
+
return expandHex(lower);
|
|
23
74
|
}
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
75
|
+
// Color functions
|
|
76
|
+
const funcMatch = lower.match(/^(\w+)\((.+)\)$/);
|
|
77
|
+
if (funcMatch) {
|
|
78
|
+
const rgb = parseColorFunction(funcMatch[1], funcMatch[2]);
|
|
79
|
+
if (rgb) {
|
|
80
|
+
const alpha = parseFunctionAlpha(funcMatch[1], funcMatch[2]);
|
|
81
|
+
if (alpha <= 0)
|
|
82
|
+
return 'default';
|
|
83
|
+
const base = rgbToColor(rgb[0], rgb[1], rgb[2]);
|
|
84
|
+
return alpha < 1 ? base + toHex(Math.round(alpha * 255)) : base;
|
|
85
|
+
}
|
|
27
86
|
}
|
|
28
87
|
// CSS named colors
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return named;
|
|
88
|
+
if (lower in CSS_NAMED_COLORS)
|
|
89
|
+
return CSS_NAMED_COLORS[lower];
|
|
32
90
|
return 'default';
|
|
33
91
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
92
|
+
/** Nominal xterm values for blending over SGR colour names. */
|
|
93
|
+
const NOMINAL_RGB = {
|
|
94
|
+
black: [0, 0, 0], red: [205, 0, 0], green: [0, 205, 0], yellow: [205, 205, 0],
|
|
95
|
+
blue: [0, 0, 238], magenta: [205, 0, 205], cyan: [0, 205, 205], white: [229, 229, 229],
|
|
96
|
+
// The terminal's actual default bg is unknowable — assume black.
|
|
97
|
+
default: [0, 0, 0],
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Composite an alpha colour (#rrggbbaa) over a base colour, returning
|
|
101
|
+
* opaque #rrggbb. Opaque over-colours return unchanged.
|
|
102
|
+
*/
|
|
103
|
+
export function blendColor(under, over) {
|
|
104
|
+
if (!over.startsWith('#') || over.length !== 9)
|
|
105
|
+
return over;
|
|
106
|
+
const alpha = parseInt(over.slice(7, 9), 16) / 255;
|
|
107
|
+
const overRgb = [
|
|
108
|
+
parseInt(over.slice(1, 3), 16),
|
|
109
|
+
parseInt(over.slice(3, 5), 16),
|
|
110
|
+
parseInt(over.slice(5, 7), 16),
|
|
111
|
+
];
|
|
112
|
+
const underRgb = under.startsWith('#')
|
|
113
|
+
? [parseInt(under.slice(1, 3), 16), parseInt(under.slice(3, 5), 16), parseInt(under.slice(5, 7), 16)]
|
|
114
|
+
: NOMINAL_RGB[under] ?? NOMINAL_RGB.default;
|
|
115
|
+
const mixed = overRgb.map((channel, i) => Math.round(channel * alpha + underRgb[i] * (1 - alpha)));
|
|
116
|
+
return '#' + mixed.map(c => toHex(c)).join('');
|
|
39
117
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
118
|
+
/** The alpha component of a colour-function value (legacy or modern). */
|
|
119
|
+
function parseFunctionAlpha(name, args) {
|
|
120
|
+
let alphaStr = null;
|
|
121
|
+
if (args.includes(',')) {
|
|
122
|
+
const parts = args.split(',').map(s => s.trim());
|
|
123
|
+
if ((name === 'rgb' || name === 'rgba' || name === 'hsl' || name === 'hsla') && parts.length >= 4) {
|
|
124
|
+
alphaStr = parts[3];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const slashIdx = args.indexOf('/');
|
|
129
|
+
if (slashIdx >= 0)
|
|
130
|
+
alphaStr = args.substring(slashIdx + 1).trim();
|
|
47
131
|
}
|
|
48
|
-
|
|
132
|
+
if (alphaStr === null)
|
|
133
|
+
return 1;
|
|
134
|
+
const value = alphaStr.endsWith('%') ? parseFloat(alphaStr) / 100 : parseFloat(alphaStr);
|
|
135
|
+
if (isNaN(value))
|
|
136
|
+
return 1;
|
|
137
|
+
return Math.max(0, Math.min(1, value));
|
|
49
138
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
139
|
+
// --- Color function parsing ---
|
|
140
|
+
function parseColorFunction(name, args) {
|
|
141
|
+
// Detect legacy comma syntax (rgb/hsl only)
|
|
142
|
+
const isLegacy = args.includes(',');
|
|
143
|
+
if (isLegacy) {
|
|
144
|
+
const parts = args.split(',').map(s => s.trim());
|
|
145
|
+
switch (name) {
|
|
146
|
+
case 'rgb':
|
|
147
|
+
case 'rgba': return parseRgbChannels(parts);
|
|
148
|
+
case 'hsl':
|
|
149
|
+
case 'hsla': return parseHslChannels(parts);
|
|
150
|
+
default: return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Modern space-separated syntax with optional / alpha
|
|
154
|
+
const slashIdx = args.indexOf('/');
|
|
155
|
+
const channelStr = slashIdx >= 0 ? args.substring(0, slashIdx) : args;
|
|
156
|
+
const parts = channelStr.trim().split(/\s+/);
|
|
157
|
+
switch (name) {
|
|
158
|
+
case 'rgb':
|
|
159
|
+
case 'rgba': return parseRgbChannels(parts);
|
|
160
|
+
case 'hsl':
|
|
161
|
+
case 'hsla': return parseHslChannels(parts);
|
|
162
|
+
case 'hwb': return parseHwbChannels(parts);
|
|
163
|
+
case 'lab': return parseLabChannels(parts);
|
|
164
|
+
case 'lch': return parseLchChannels(parts);
|
|
165
|
+
case 'oklab': return parseOklabChannels(parts);
|
|
166
|
+
case 'oklch': return parseOklchChannels(parts);
|
|
167
|
+
default: return null;
|
|
168
|
+
}
|
|
68
169
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
170
|
+
// --- Channel parsers ---
|
|
171
|
+
function parseRgbChannels(parts) {
|
|
172
|
+
if (parts.length < 3)
|
|
173
|
+
return null;
|
|
174
|
+
const r = parseChannelValue(parts[0], 255);
|
|
175
|
+
const g = parseChannelValue(parts[1], 255);
|
|
176
|
+
const b = parseChannelValue(parts[2], 255);
|
|
177
|
+
if (r == null || g == null || b == null)
|
|
178
|
+
return null;
|
|
179
|
+
return [clamp(r, 0, 255), clamp(g, 0, 255), clamp(b, 0, 255)];
|
|
72
180
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
181
|
+
function parseHslChannels(parts) {
|
|
182
|
+
if (parts.length < 3)
|
|
183
|
+
return null;
|
|
184
|
+
const h = parseAngle(parts[0]);
|
|
185
|
+
const s = parsePercent(parts[1]);
|
|
186
|
+
const l = parsePercent(parts[2]);
|
|
187
|
+
if (h == null || s == null || l == null)
|
|
188
|
+
return null;
|
|
189
|
+
return hslToRgb(h, s, l);
|
|
190
|
+
}
|
|
191
|
+
function parseHwbChannels(parts) {
|
|
192
|
+
if (parts.length < 3)
|
|
193
|
+
return null;
|
|
194
|
+
const h = parseAngle(parts[0]);
|
|
195
|
+
let w = parsePercent(parts[1]);
|
|
196
|
+
let b = parsePercent(parts[2]);
|
|
197
|
+
if (h == null || w == null || b == null)
|
|
198
|
+
return null;
|
|
199
|
+
// Normalise when w + b > 1
|
|
200
|
+
if (w + b > 1) {
|
|
201
|
+
const t = w + b;
|
|
202
|
+
w /= t;
|
|
203
|
+
b /= t;
|
|
204
|
+
}
|
|
205
|
+
return hwbToRgb(h, w, b);
|
|
206
|
+
}
|
|
207
|
+
function parseLabChannels(parts) {
|
|
208
|
+
if (parts.length < 3)
|
|
209
|
+
return null;
|
|
210
|
+
const L = parseChannelValue(parts[0], 100);
|
|
211
|
+
const a = parseFloat(parts[1]);
|
|
212
|
+
const b = parseFloat(parts[2]);
|
|
213
|
+
if (L == null || isNaN(a) || isNaN(b))
|
|
214
|
+
return null;
|
|
215
|
+
return labToRgb(L, a, b);
|
|
216
|
+
}
|
|
217
|
+
function parseLchChannels(parts) {
|
|
218
|
+
if (parts.length < 3)
|
|
219
|
+
return null;
|
|
220
|
+
const L = parseChannelValue(parts[0], 100);
|
|
221
|
+
const C = parseFloat(parts[1]);
|
|
222
|
+
const H = parseAngle(parts[2]);
|
|
223
|
+
if (L == null || isNaN(C) || H == null)
|
|
224
|
+
return null;
|
|
225
|
+
// LCH → LAB
|
|
226
|
+
const a = C * Math.cos(H * Math.PI / 180);
|
|
227
|
+
const b = C * Math.sin(H * Math.PI / 180);
|
|
228
|
+
return labToRgb(L, a, b);
|
|
229
|
+
}
|
|
230
|
+
function parseOklabChannels(parts) {
|
|
231
|
+
if (parts.length < 3)
|
|
232
|
+
return null;
|
|
233
|
+
const L = parseChannelValue(parts[0], 1);
|
|
234
|
+
const a = parseFloat(parts[1]);
|
|
235
|
+
const b = parseFloat(parts[2]);
|
|
236
|
+
if (L == null || isNaN(a) || isNaN(b))
|
|
237
|
+
return null;
|
|
238
|
+
return oklabToRgb(L, a, b);
|
|
239
|
+
}
|
|
240
|
+
function parseOklchChannels(parts) {
|
|
241
|
+
if (parts.length < 3)
|
|
242
|
+
return null;
|
|
243
|
+
const L = parseChannelValue(parts[0], 1);
|
|
244
|
+
const C = parseFloat(parts[1]);
|
|
245
|
+
const H = parseAngle(parts[2]);
|
|
246
|
+
if (L == null || isNaN(C) || H == null)
|
|
247
|
+
return null;
|
|
248
|
+
// OKLCH → OKLAB
|
|
249
|
+
const a = C * Math.cos(H * Math.PI / 180);
|
|
250
|
+
const b = C * Math.sin(H * Math.PI / 180);
|
|
251
|
+
return oklabToRgb(L, a, b);
|
|
75
252
|
}
|
|
253
|
+
// --- Value parsers ---
|
|
254
|
+
/** Parse a number or percentage. maxVal is what 100% maps to. */
|
|
255
|
+
function parseChannelValue(s, maxVal) {
|
|
256
|
+
s = s.trim();
|
|
257
|
+
if (s === 'none')
|
|
258
|
+
return 0;
|
|
259
|
+
if (s.endsWith('%')) {
|
|
260
|
+
const v = parseFloat(s);
|
|
261
|
+
return isNaN(v) ? null : (v / 100) * maxVal;
|
|
262
|
+
}
|
|
263
|
+
const v = parseFloat(s);
|
|
264
|
+
return isNaN(v) ? null : v;
|
|
265
|
+
}
|
|
266
|
+
/** Parse a percentage to 0-1. */
|
|
267
|
+
function parsePercent(s) {
|
|
268
|
+
s = s.trim();
|
|
269
|
+
if (s === 'none')
|
|
270
|
+
return 0;
|
|
271
|
+
if (s.endsWith('%')) {
|
|
272
|
+
const v = parseFloat(s);
|
|
273
|
+
return isNaN(v) ? null : v / 100;
|
|
274
|
+
}
|
|
275
|
+
// Allow raw 0-1 values for oklab/oklch
|
|
276
|
+
const v = parseFloat(s);
|
|
277
|
+
return isNaN(v) ? null : v;
|
|
278
|
+
}
|
|
279
|
+
/** Parse an angle value with unit support. Returns degrees. */
|
|
280
|
+
function parseAngle(s) {
|
|
281
|
+
s = s.trim();
|
|
282
|
+
if (s === 'none')
|
|
283
|
+
return 0;
|
|
284
|
+
if (s.endsWith('grad'))
|
|
285
|
+
return parseFloat(s) * 0.9;
|
|
286
|
+
if (s.endsWith('rad'))
|
|
287
|
+
return parseFloat(s) * 180 / Math.PI;
|
|
288
|
+
if (s.endsWith('turn'))
|
|
289
|
+
return parseFloat(s) * 360;
|
|
290
|
+
if (s.endsWith('deg'))
|
|
291
|
+
return parseFloat(s);
|
|
292
|
+
// Bare number = degrees
|
|
293
|
+
const v = parseFloat(s);
|
|
294
|
+
return isNaN(v) ? null : v;
|
|
295
|
+
}
|
|
296
|
+
// --- Colour space conversions ---
|
|
76
297
|
function hslToRgb(h, s, l) {
|
|
298
|
+
h = ((h % 360) + 360) % 360; // normalise hue
|
|
77
299
|
if (s === 0) {
|
|
78
300
|
const v = Math.round(l * 255);
|
|
79
301
|
return [v, v, v];
|
|
@@ -112,7 +334,94 @@ function hslToRgb(h, s, l) {
|
|
|
112
334
|
Math.round((b + m) * 255),
|
|
113
335
|
];
|
|
114
336
|
}
|
|
115
|
-
|
|
337
|
+
function hwbToRgb(h, w, b) {
|
|
338
|
+
// Get pure hue colour
|
|
339
|
+
const [hr, hg, hb] = hslToRgb(h, 1, 0.5);
|
|
340
|
+
const factor = 1 - w - b;
|
|
341
|
+
return [
|
|
342
|
+
Math.round((hr / 255 * factor + w) * 255),
|
|
343
|
+
Math.round((hg / 255 * factor + w) * 255),
|
|
344
|
+
Math.round((hb / 255 * factor + w) * 255),
|
|
345
|
+
];
|
|
346
|
+
}
|
|
347
|
+
/** OKLAB → sRGB */
|
|
348
|
+
function oklabToRgb(L, a, b) {
|
|
349
|
+
// Oklab → LMS
|
|
350
|
+
const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
|
|
351
|
+
const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
|
|
352
|
+
const s_ = L - 0.0894841775 * a - 1.2914855480 * b;
|
|
353
|
+
const l = l_ * l_ * l_;
|
|
354
|
+
const m = m_ * m_ * m_;
|
|
355
|
+
const s = s_ * s_ * s_;
|
|
356
|
+
// LMS → linear sRGB
|
|
357
|
+
const lr = 4.0767416621 * l - 3.3077363322 * m + 0.2309101289 * s;
|
|
358
|
+
const lg = -1.2684380046 * l + 2.6097574011 * m - 0.3413193761 * s;
|
|
359
|
+
const lb = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s;
|
|
360
|
+
// Linear sRGB → sRGB (gamma) and scale to 0-255
|
|
361
|
+
return [
|
|
362
|
+
Math.round(clamp(linearToSrgb(lr), 0, 1) * 255),
|
|
363
|
+
Math.round(clamp(linearToSrgb(lg), 0, 1) * 255),
|
|
364
|
+
Math.round(clamp(linearToSrgb(lb), 0, 1) * 255),
|
|
365
|
+
];
|
|
366
|
+
}
|
|
367
|
+
/** LAB (CIELAB) → sRGB */
|
|
368
|
+
function labToRgb(L, a, b) {
|
|
369
|
+
// LAB → XYZ-D50
|
|
370
|
+
const fy = (L + 16) / 116;
|
|
371
|
+
const fx = a / 500 + fy;
|
|
372
|
+
const fz = fy - b / 200;
|
|
373
|
+
const D50_X = 0.3457 / 0.3585;
|
|
374
|
+
const D50_Y = 1.0;
|
|
375
|
+
const D50_Z = (1 - 0.3457 - 0.3585) / 0.3585;
|
|
376
|
+
const x = (fx > 6 / 29 ? fx * fx * fx : (116 * fx - 16) / 903.3) * D50_X;
|
|
377
|
+
const y = (L > 8 ? ((L + 16) / 116) ** 3 : L / 903.3) * D50_Y;
|
|
378
|
+
const z = (fz > 6 / 29 ? fz * fz * fz : (116 * fz - 16) / 903.3) * D50_Z;
|
|
379
|
+
// XYZ-D50 → XYZ-D65 (Bradford chromatic adaptation)
|
|
380
|
+
const xd = 0.9554734527 * x - 0.0230985368 * y + 0.0632593086 * z;
|
|
381
|
+
const yd = -0.0283697093 * x + 1.0099954580 * y + 0.0210415381 * z;
|
|
382
|
+
const zd = 0.0123140016 * x - 0.0205076964 * y + 1.3303899330 * z;
|
|
383
|
+
// XYZ-D65 → linear sRGB
|
|
384
|
+
const lr = 3.2404541621 * xd - 1.5371385940 * yd - 0.4985314096 * zd;
|
|
385
|
+
const lg = -0.9692660305 * xd + 1.8760108454 * yd + 0.0415560175 * zd;
|
|
386
|
+
const lb = 0.0556434309 * xd - 0.2040259135 * yd + 1.0572251882 * zd;
|
|
387
|
+
return [
|
|
388
|
+
Math.round(clamp(linearToSrgb(lr), 0, 1) * 255),
|
|
389
|
+
Math.round(clamp(linearToSrgb(lg), 0, 1) * 255),
|
|
390
|
+
Math.round(clamp(linearToSrgb(lb), 0, 1) * 255),
|
|
391
|
+
];
|
|
392
|
+
}
|
|
393
|
+
/** Linear sRGB → sRGB gamma correction */
|
|
394
|
+
function linearToSrgb(v) {
|
|
395
|
+
if (v <= 0.0031308)
|
|
396
|
+
return 12.92 * v;
|
|
397
|
+
return 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
|
|
398
|
+
}
|
|
399
|
+
// --- Helpers ---
|
|
400
|
+
function clamp(v, min, max) {
|
|
401
|
+
return Math.max(min, Math.min(max, v));
|
|
402
|
+
}
|
|
403
|
+
function expandHex(hex) {
|
|
404
|
+
const h = hex.slice(1);
|
|
405
|
+
if (h.length === 3)
|
|
406
|
+
return '#' + h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
|
407
|
+
if (h.length === 4)
|
|
408
|
+
return '#' + h[0] + h[0] + h[1] + h[1] + h[2] + h[2] + h[3] + h[3];
|
|
409
|
+
return hex;
|
|
410
|
+
}
|
|
411
|
+
function rgbToColor(r, g, b) {
|
|
412
|
+
// Computed colours (rgb(), hsl(), oklch(), …) stay truecolor like hex —
|
|
413
|
+
// only keywords map to themeable ANSI names.
|
|
414
|
+
return '#' + toHex(r) + toHex(g) + toHex(b);
|
|
415
|
+
}
|
|
416
|
+
function toHex(n) {
|
|
417
|
+
return Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, '0');
|
|
418
|
+
}
|
|
419
|
+
// --- ANSI colours ---
|
|
420
|
+
const ANSI_COLORS = {
|
|
421
|
+
black: 'black', red: 'red', green: 'green', yellow: 'yellow',
|
|
422
|
+
blue: 'blue', magenta: 'magenta', cyan: 'cyan', white: 'white',
|
|
423
|
+
};
|
|
424
|
+
// --- CSS named colours (all 148) ---
|
|
116
425
|
const CSS_NAMED_COLORS = {
|
|
117
426
|
aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4',
|
|
118
427
|
azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', blanchedalmond: '#ffebcd',
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { TermNode } from '../renderer/node.js';
|
|
2
|
+
import { NodeMap } from '../utils/node-map.js';
|
|
3
|
+
export type StyleMap = NodeMap<ResolvedStyle>;
|
|
2
4
|
import { CSSStyleSheet } from './parser.js';
|
|
3
5
|
import { type MediaContext } from './media.js';
|
|
4
6
|
export interface ResolvedStyle {
|
|
@@ -9,7 +11,7 @@ export interface ResolvedStyle {
|
|
|
9
11
|
underline: boolean;
|
|
10
12
|
strikethrough: boolean;
|
|
11
13
|
dim: boolean;
|
|
12
|
-
display: 'block' | 'inline' | 'inline-block' | 'flex' | 'grid' | 'table' | 'table-row' | 'table-cell' | 'none' | 'contents';
|
|
14
|
+
display: 'block' | 'inline' | 'inline-block' | 'flex' | 'grid' | 'table' | 'inline-table' | 'table-row' | 'table-cell' | 'table-row-group' | 'table-header-group' | 'table-footer-group' | 'table-caption' | 'table-column-group' | 'table-column' | 'none' | 'contents';
|
|
13
15
|
flexDirection: 'row' | 'column' | 'row-reverse' | 'column-reverse';
|
|
14
16
|
justifyContent: 'start' | 'end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
|
|
15
17
|
alignItems: 'start' | 'end' | 'center' | 'stretch';
|
|
@@ -36,19 +38,36 @@ export interface ResolvedStyle {
|
|
|
36
38
|
order: number;
|
|
37
39
|
gridTemplateColumns: string | null;
|
|
38
40
|
gridTemplateRows: string | null;
|
|
41
|
+
gridColumnStart: number | null;
|
|
42
|
+
gridColumnEnd: number | null;
|
|
43
|
+
gridColumnSpan: number | null;
|
|
44
|
+
gridRowStart: number | null;
|
|
45
|
+
gridRowEnd: number | null;
|
|
46
|
+
gridRowSpan: number | null;
|
|
47
|
+
gridTemplateAreas: string | null;
|
|
48
|
+
gridArea: string | null;
|
|
39
49
|
animationName: string | null;
|
|
40
50
|
animationDuration: number;
|
|
41
51
|
animationIterationCount: number;
|
|
42
|
-
|
|
52
|
+
animationTimingFunction: string;
|
|
53
|
+
transitionProperty: string | null;
|
|
54
|
+
transitionDuration: number;
|
|
55
|
+
transitionTimingFunction: string;
|
|
56
|
+
borderStyle: 'none' | 'single' | 'double' | 'rounded' | 'heavy' | 'ascii' | 'eighth-cell-inner' | 'eighth-cell-outer' | 'half-cell-inner' | 'half-cell-outer' | 'full-cell';
|
|
57
|
+
borderCorner: 'none' | 'h' | 'v';
|
|
43
58
|
borderColor: string;
|
|
44
59
|
borderTop: boolean;
|
|
45
60
|
borderRight: boolean;
|
|
46
61
|
borderBottom: boolean;
|
|
47
62
|
borderLeft: boolean;
|
|
63
|
+
boxSizing: 'border-box' | 'content-box';
|
|
48
64
|
overflow: 'visible' | 'hidden' | 'scroll' | 'auto';
|
|
49
65
|
textOverflow: 'clip' | 'ellipsis' | 'ellipsis-middle';
|
|
50
66
|
whiteSpace: 'normal' | 'nowrap' | 'pre';
|
|
67
|
+
wordBreak: 'normal' | 'break-all';
|
|
68
|
+
opacity: number;
|
|
51
69
|
textAlign: 'left' | 'center' | 'right';
|
|
70
|
+
textTransform: 'none' | 'uppercase' | 'lowercase' | 'capitalize';
|
|
52
71
|
position: 'static' | 'relative' | 'absolute' | 'fixed';
|
|
53
72
|
top: number | null;
|
|
54
73
|
right: number | null;
|
|
@@ -56,8 +75,16 @@ export interface ResolvedStyle {
|
|
|
56
75
|
left: number | null;
|
|
57
76
|
zIndex: number;
|
|
58
77
|
visibility: 'visible' | 'hidden';
|
|
78
|
+
captionSide: 'top' | 'bottom';
|
|
79
|
+
tableLayout: 'auto' | 'fixed';
|
|
80
|
+
verticalAlign: 'top' | 'middle' | 'bottom' | 'baseline';
|
|
81
|
+
borderCollapse: 'separate' | 'collapse';
|
|
82
|
+
borderSpacingH: number;
|
|
83
|
+
borderSpacingV: number;
|
|
84
|
+
emptyCells: 'show' | 'hide';
|
|
59
85
|
}
|
|
60
86
|
export declare function defaultStyle(tag?: string): ResolvedStyle;
|
|
61
87
|
export declare function resolveStyles(root: TermNode, stylesheet: CSSStyleSheet, media?: MediaContext, availWidth?: number, availHeight?: number): Map<number, ResolvedStyle>;
|
|
62
88
|
export declare function filterByMedia(stylesheet: CSSStyleSheet, context: MediaContext): CSSStyleSheet;
|
|
63
|
-
export declare function resolveNode(node: TermNode, stylesheet: CSSStyleSheet, styles: Map<number, ResolvedStyle>, variables: Map<number, Map<string, string
|
|
89
|
+
export declare function resolveNode(node: TermNode, stylesheet: CSSStyleSheet, styles: Map<number, ResolvedStyle>, variables: Map<number, Map<string, string>>, scheme?: 'dark' | 'light'): void;
|
|
90
|
+
export declare function applyDeclaration(style: ResolvedStyle, property: string, value: string, scheme?: 'dark' | 'light'): void;
|