@code-yeongyu/senpi-tui 2026.9.30 → 2026.10.1-2
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 +38 -0
- package/dist/autocomplete.js +38 -14
- package/dist/colors.d.ts +73 -0
- package/dist/colors.js +273 -0
- package/dist/components/box.js +5 -3
- package/dist/components/editor.js +9 -6
- package/dist/components/latex.js +5 -0
- package/dist/fuzzy.js +23 -22
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/native-platform.d.ts +2 -0
- package/dist/oklab.d.ts +43 -0
- package/dist/oklab.js +210 -0
- package/dist/terminal-capabilities.js +1 -1
- package/dist/terminal-colors.d.ts +19 -2
- package/dist/terminal-colors.js +12 -7
- package/dist/terminal-image.d.ts +3 -1
- package/dist/terminal-image.js +33 -9
- package/dist/terminal.d.ts +3 -0
- package/dist/terminal.js +15 -8
- package/dist/tui-alt-screen.d.ts +12 -7
- package/dist/tui-alt-screen.js +37 -17
- package/dist/tui.d.ts +18 -17
- package/dist/tui.js +79 -62
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +108 -60
- package/dist/wheel-scroll.d.ts +23 -0
- package/dist/wheel-scroll.js +70 -0
- package/native/clipboard.h +40 -2
- package/native/darwin/README.md +1 -1
- package/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/native/darwin/src/darwin-platform.m +39 -0
- package/native/napi.h +2 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CellDimensions, ImageDimensions } from "./terminal-image.ts";
|
|
|
2
2
|
export { Marked, type Token, type Tokens } from "marked";
|
|
3
3
|
export { AltScreenSearchComponent, type AltScreenSearchMatch, type AltScreenSearchSegment, findAltScreenSearchMatches, getAltScreenSearchMatchKey, } from "./alt-screen-search.ts";
|
|
4
4
|
export { type AutocompleteItem, type AutocompleteProvider, type AutocompleteSuggestions, CombinedAutocompleteProvider, type SlashCommand, } from "./autocomplete.ts";
|
|
5
|
+
export { backgroundAnsi, type Color, type ColorMixSpace, colorToHex, colorToOkhsl, colorToOklch, colorToRgb, foregroundAnsi, type IndexedColor, indexedColor, mixColors, type OkhslChannels, type OklchChannels, type OklchColorValue, okhslColor, oklchColor, parseColor, type RgbColorValue, rgbColor, styleText, styleTextWithAnsi, type TerminalColorMode, type TextAttributes, type TextStyle, } from "./colors.ts";
|
|
5
6
|
export { Box } from "./components/box.ts";
|
|
6
7
|
export { CancellableLoader } from "./components/cancellable-loader.ts";
|
|
7
8
|
export { Editor, type EditorOptions, type EditorTheme, } from "./components/editor.ts";
|
|
@@ -25,15 +26,17 @@ export { getKeybindings, type Keybinding, type KeybindingConflict, type Keybindi
|
|
|
25
26
|
export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, Key, type KeyEventType, type KeyId, matchesKey, parseKey, setKittyProtocolActive, } from "./keys.ts";
|
|
26
27
|
export { decodeMouseButton, isMouseSequence, MOUSE_TRACKING, MouseClickSynthesizer, parseSgrMouseEvent, parseWheelEvent, type SgrMouseEvent, toTuiMouseEvent, type WheelEvent, } from "./mouse-input.ts";
|
|
27
28
|
export { getNativeClipboard, type NativeClipboard } from "./native-platform.ts";
|
|
29
|
+
export { oklabToOkhslLightness } from "./oklab.ts";
|
|
28
30
|
export { type EditorPasteState, expandPasteMarkers } from "./paste-markers.ts";
|
|
29
31
|
export { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from "./stdin-buffer.ts";
|
|
30
32
|
export { type CursorPosition, ProcessTerminal, type ProcessTerminalOptions, type Terminal } from "./terminal.ts";
|
|
31
|
-
export {
|
|
33
|
+
export { parseTerminalColorSchemeReport, type RgbColor, type TerminalColorScheme, type TerminalColors, } from "./terminal-colors.ts";
|
|
32
34
|
export declare function calculateImageRows(imageDimensions: ImageDimensions, targetWidthCells: number, cellDimensions?: CellDimensions): number;
|
|
33
|
-
export { allocateImageId, buildKittyPlaceholderRow, type CellDimensions, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, type ImageDimensions, type ImageProtocol, type ImageRenderOptions, imageFallback, KITTY_PLACEHOLDER_MAX, outerKittyGraphicsMode, renderImage, resetCapabilitiesCache, setCapabilities, setCapabilityOverrides, setCellDimensions, type TerminalCapabilities, type TmuxPassthroughState, wrapTmuxPassthrough, } from "./terminal-image.ts";
|
|
35
|
+
export { allocateImageId, buildKittyPlaceholderRow, type CellDimensions, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getTerminalColorMode, getWebpDimensions, hyperlink, type ImageDimensions, type ImageProtocol, type ImageRenderOptions, imageFallback, KITTY_PLACEHOLDER_MAX, outerKittyGraphicsMode, renderImage, resetCapabilitiesCache, setCapabilities, setCapabilityOverrides, setCellDimensions, type TerminalCapabilities, type TmuxPassthroughState, wrapTmuxPassthrough, } from "./terminal-image.ts";
|
|
34
36
|
export { sanitizeTerminalLabel, shortenImagePath } from "./terminal-text.ts";
|
|
35
37
|
export { type Component, Container, CURSOR_MARKER, compositeTuiLine, type Focusable, isFocusable, isViewportTUI, type OverlayAnchor, type OverlayBounds, type OverlayHandle, type OverlayMargin, type OverlayOptions, type OverlayUnfocusOptions, type SizeValue, TUI, type TuiInputListener, type TuiInputListenerResult, type TuiMode, type TuiMouseButton, type TuiMouseEvent, type TuiMouseEventResult, type TuiMouseEventType, type TuiStopOptions, type ViewportTUI, } from "./tui.ts";
|
|
36
38
|
export { TuiAltScreen, type TuiAltScreenOptions } from "./tui-alt-screen.ts";
|
|
37
39
|
export { TuiMainScreen, type TuiMainScreenRenderState } from "./tui-main-screen.ts";
|
|
38
40
|
export { getGraphemeSegmenter, getOsc8LinkAtColumn, getWordSegmenter, sliceByColumn, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "./utils.ts";
|
|
41
|
+
export type { WheelScrollLines } from "./wheel-scroll.ts";
|
|
39
42
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export { Marked } from "marked";
|
|
|
5
5
|
export { AltScreenSearchComponent, findAltScreenSearchMatches, getAltScreenSearchMatchKey, } from "./alt-screen-search.js";
|
|
6
6
|
// Autocomplete support
|
|
7
7
|
export { CombinedAutocompleteProvider, } from "./autocomplete.js";
|
|
8
|
+
// Colors and styling
|
|
9
|
+
export { backgroundAnsi, colorToHex, colorToOkhsl, colorToOklch, colorToRgb, foregroundAnsi, indexedColor, mixColors, okhslColor, oklchColor, parseColor, rgbColor, styleText, styleTextWithAnsi, } from "./colors.js";
|
|
8
10
|
// Components
|
|
9
11
|
export { Box } from "./components/box.js";
|
|
10
12
|
export { CancellableLoader } from "./components/cancellable-loader.js";
|
|
@@ -33,18 +35,19 @@ export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive,
|
|
|
33
35
|
export { decodeMouseButton, isMouseSequence, MOUSE_TRACKING, MouseClickSynthesizer, parseSgrMouseEvent, parseWheelEvent, toTuiMouseEvent, } from "./mouse-input.js";
|
|
34
36
|
// Native platform integration
|
|
35
37
|
export { getNativeClipboard } from "./native-platform.js";
|
|
38
|
+
export { oklabToOkhslLightness } from "./oklab.js";
|
|
36
39
|
export { expandPasteMarkers } from "./paste-markers.js";
|
|
37
40
|
// Input buffering for batch splitting
|
|
38
41
|
export { StdinBuffer } from "./stdin-buffer.js";
|
|
39
42
|
// Terminal interface and implementations
|
|
40
43
|
export { ProcessTerminal } from "./terminal.js";
|
|
41
44
|
// Terminal colors
|
|
42
|
-
export {
|
|
45
|
+
export { parseTerminalColorSchemeReport, } from "./terminal-colors.js";
|
|
43
46
|
// Terminal image support
|
|
44
47
|
export function calculateImageRows(imageDimensions, targetWidthCells, cellDimensions = { widthPx: 9, heightPx: 18 }) {
|
|
45
48
|
return calculateImageCellSize(imageDimensions, targetWidthCells, undefined, cellDimensions).rows;
|
|
46
49
|
}
|
|
47
|
-
export { allocateImageId, buildKittyPlaceholderRow, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, imageFallback, KITTY_PLACEHOLDER_MAX, outerKittyGraphicsMode, renderImage, resetCapabilitiesCache, setCapabilities, setCapabilityOverrides, setCellDimensions, wrapTmuxPassthrough, } from "./terminal-image.js";
|
|
50
|
+
export { allocateImageId, buildKittyPlaceholderRow, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getTerminalColorMode, getWebpDimensions, hyperlink, imageFallback, KITTY_PLACEHOLDER_MAX, outerKittyGraphicsMode, renderImage, resetCapabilitiesCache, setCapabilities, setCapabilityOverrides, setCellDimensions, wrapTmuxPassthrough, } from "./terminal-image.js";
|
|
48
51
|
export { sanitizeTerminalLabel, shortenImagePath } from "./terminal-text.js";
|
|
49
52
|
export { Container, CURSOR_MARKER, compositeTuiLine, isFocusable, isViewportTUI, TUI, } from "./tui.js";
|
|
50
53
|
export { TuiAltScreen } from "./tui-alt-screen.js";
|
|
@@ -4,6 +4,8 @@ export interface NativeClipboard {
|
|
|
4
4
|
getText(): Promise<string | null | undefined>;
|
|
5
5
|
/** Undefined means unavailable, null means no image; transfer failures reject. */
|
|
6
6
|
getImage(): Promise<Uint8Array | null | undefined>;
|
|
7
|
+
/** POSIX paths of file URLs on the clipboard. Undefined means unsupported, null means no files. */
|
|
8
|
+
getFilePaths?(): Promise<string[] | null | undefined>;
|
|
7
9
|
/** Linux uses command-line tools to retain clipboard ownership instead. */
|
|
8
10
|
setText?(text: string): Promise<void>;
|
|
9
11
|
}
|
package/dist/oklab.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oklab and OKHSL <-> sRGB conversion. `colors.ts` builds its OKLCH, OKHSL, and color mixing on it.
|
|
3
|
+
*
|
|
4
|
+
* Oklab and OKHSL are Björn Ottosson's color spaces; OKHSL's saturation is relative to the sRGB gamut at
|
|
5
|
+
* each hue and lightness. This is a port of his reference implementation (https://bottosson.github.io/posts/colorpicker/),
|
|
6
|
+
* Copyright (c) 2021 Björn Ottosson, used under the MIT license:
|
|
7
|
+
*
|
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
9
|
+
* associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
10
|
+
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
12
|
+
* following conditions: The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
|
14
|
+
* KIND, EXPRESS OR IMPLIED.
|
|
15
|
+
*/
|
|
16
|
+
import type { RgbColor } from "./terminal-colors.ts";
|
|
17
|
+
type Vector = [number, number, number];
|
|
18
|
+
/** Oklab lightness to OKHSL lightness. */
|
|
19
|
+
export declare const oklabToOkhslLightness: (x: number) => number;
|
|
20
|
+
/** Oklab [L, a, b] to linear sRGB [r, g, b] (0-1, may leave the gamut). */
|
|
21
|
+
export declare function oklabToLinearSrgb(lab: Vector): Vector;
|
|
22
|
+
/** sRGB channels (0-255) to Oklab [L, a, b]. */
|
|
23
|
+
export declare function rgbToOklab({ r, g, b }: RgbColor): Vector;
|
|
24
|
+
/** Linear sRGB [r, g, b] to sRGB channels (0-255, rounded), clipping out-of-gamut channels. */
|
|
25
|
+
export declare function linearSrgbToRgb(linear: Vector): RgbColor;
|
|
26
|
+
/**
|
|
27
|
+
* Convert OKHSL to sRGB channels (0-255, rounded), clipping out-of-gamut channels.
|
|
28
|
+
* @param hue Hue in degrees.
|
|
29
|
+
* @param saturation Saturation, 0-1.
|
|
30
|
+
* @param lightness Lightness, 0-1.
|
|
31
|
+
*/
|
|
32
|
+
export declare function okhslToRgb(hue: number, saturation: number, lightness: number): RgbColor;
|
|
33
|
+
/**
|
|
34
|
+
* Convert sRGB channels (0-255) to OKHSL.
|
|
35
|
+
* @returns Hue `h` in degrees (0 for grays), saturation `s` and lightness `l` 0-1.
|
|
36
|
+
*/
|
|
37
|
+
export declare function rgbToOkhsl(rgb: RgbColor): {
|
|
38
|
+
h: number;
|
|
39
|
+
s: number;
|
|
40
|
+
l: number;
|
|
41
|
+
};
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=oklab.d.ts.map
|
package/dist/oklab.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oklab and OKHSL <-> sRGB conversion. `colors.ts` builds its OKLCH, OKHSL, and color mixing on it.
|
|
3
|
+
*
|
|
4
|
+
* Oklab and OKHSL are Björn Ottosson's color spaces; OKHSL's saturation is relative to the sRGB gamut at
|
|
5
|
+
* each hue and lightness. This is a port of his reference implementation (https://bottosson.github.io/posts/colorpicker/),
|
|
6
|
+
* Copyright (c) 2021 Björn Ottosson, used under the MIT license:
|
|
7
|
+
*
|
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
9
|
+
* associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
10
|
+
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
12
|
+
* following conditions: The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
* copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
|
14
|
+
* KIND, EXPRESS OR IMPLIED.
|
|
15
|
+
*/
|
|
16
|
+
const multiply = (m, [x, y, z]) => m.map((row) => row[0] * x + row[1] * y + row[2] * z);
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// OKHSL <-> sRGB
|
|
19
|
+
// ============================================================================
|
|
20
|
+
const LINEAR_SRGB_TO_LMS = [
|
|
21
|
+
[0.4122214694707629, 0.5363325372617349, 0.0514459932675022],
|
|
22
|
+
[0.2119034958178251, 0.6806995506452344, 0.1073969535369405],
|
|
23
|
+
[0.0883024591900564, 0.2817188391361215, 0.6299787016738222],
|
|
24
|
+
];
|
|
25
|
+
const LMS_TO_LAB = [
|
|
26
|
+
[0.210454268309314, 0.793617774702305, -0.0040720430116193],
|
|
27
|
+
[1.9779985324311684, -2.42859224204858, 0.450593709617411],
|
|
28
|
+
[0.0259040424655478, 0.7827717124575296, -0.8086757549230774],
|
|
29
|
+
];
|
|
30
|
+
const LAB_TO_LMS = [
|
|
31
|
+
[1, 0.3963377773761749, 0.2158037573099136],
|
|
32
|
+
[1, -0.1055613458156586, -0.0638541728258133],
|
|
33
|
+
[1, -0.0894841775298119, -1.2914855480194092],
|
|
34
|
+
];
|
|
35
|
+
const LMS_TO_LINEAR_SRGB = [
|
|
36
|
+
[4.0767416360759583, -3.3077115392580629, 0.2309699031821043],
|
|
37
|
+
[-1.2684379732850315, 2.6097573492876882, -0.341319376002657],
|
|
38
|
+
[-0.0041960761386756, -0.7034186179359362, 1.7076146940746117],
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Per sRGB channel (red, green, blue): the (a, b) half-plane where that channel clips
|
|
42
|
+
* first, and the polynomial approximating the maximum saturation there.
|
|
43
|
+
*/
|
|
44
|
+
const SATURATION_FIT = [
|
|
45
|
+
[
|
|
46
|
+
[-1.8817031, -0.80936501],
|
|
47
|
+
[1.19086277, 1.76576728, 0.59662641, 0.75515197, 0.56771245],
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
[1.8144408, -1.19445267],
|
|
51
|
+
[0.73956515, -0.45954404, 0.08285427, 0.12541073, -0.14503204],
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
[0.13110758, 1.81333971],
|
|
55
|
+
[1.35733652, -0.00915799, -1.1513021, -0.50559606, 0.00692167],
|
|
56
|
+
],
|
|
57
|
+
];
|
|
58
|
+
const K1 = 0.206;
|
|
59
|
+
const K2 = 0.03;
|
|
60
|
+
const K3 = (1 + K1) / (1 + K2);
|
|
61
|
+
/** Oklab lightness to OKHSL lightness. */
|
|
62
|
+
export const oklabToOkhslLightness = (x) => 0.5 * (K3 * x - K1 + Math.sqrt((K3 * x - K1) ** 2 + 4 * K2 * K3 * x));
|
|
63
|
+
/** OKHSL lightness to Oklab lightness. */
|
|
64
|
+
const okhslToOklabLightness = (x) => (x * x + K1 * x) / (K3 * (x + K2));
|
|
65
|
+
/** sRGB transfer function: linear to encoded channel, both 0-1. */
|
|
66
|
+
const linearToSrgb = (value) => value > 0.0031308 ? 1.055 * value ** (1 / 2.4) - 0.055 : 12.92 * value;
|
|
67
|
+
/** Inverse sRGB transfer function: encoded to linear channel, both 0-1. */
|
|
68
|
+
const srgbToLinear = (value) => (value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4);
|
|
69
|
+
/** Oklab [L, a, b] to linear sRGB [r, g, b] (0-1, may leave the gamut). */
|
|
70
|
+
export function oklabToLinearSrgb(lab) {
|
|
71
|
+
return multiply(LMS_TO_LINEAR_SRGB, multiply(LAB_TO_LMS, lab).map((value) => value ** 3));
|
|
72
|
+
}
|
|
73
|
+
/** Linear sRGB [r, g, b] (0-1) to Oklab [L, a, b]. */
|
|
74
|
+
function linearSrgbToOklab(rgb) {
|
|
75
|
+
return multiply(LMS_TO_LAB, multiply(LINEAR_SRGB_TO_LMS, rgb).map(Math.cbrt));
|
|
76
|
+
}
|
|
77
|
+
/** sRGB channels (0-255) to Oklab [L, a, b]. */
|
|
78
|
+
export function rgbToOklab({ r, g, b }) {
|
|
79
|
+
return linearSrgbToOklab([r / 255, g / 255, b / 255].map(srgbToLinear));
|
|
80
|
+
}
|
|
81
|
+
/** Linear sRGB [r, g, b] to sRGB channels (0-255, rounded), clipping out-of-gamut channels. */
|
|
82
|
+
export function linearSrgbToRgb(linear) {
|
|
83
|
+
const [r, g, b] = linear.map((value) => Math.round(Math.min(1, Math.max(0, linearToSrgb(value))) * 255));
|
|
84
|
+
return { r, g, b };
|
|
85
|
+
}
|
|
86
|
+
/** Rate of change of each cube-root LMS component along a chroma direction (a, b). */
|
|
87
|
+
function lmsSlopes(a, b) {
|
|
88
|
+
return [LAB_TO_LMS[0], LAB_TO_LMS[1], LAB_TO_LMS[2]].map((row) => row[1] * a + row[2] * b);
|
|
89
|
+
}
|
|
90
|
+
/** Largest saturation (C/L) inside sRGB for hue (a, b): polynomial fit plus one Halley step. */
|
|
91
|
+
function maxSaturation(a, b) {
|
|
92
|
+
const channel = SATURATION_FIT.findIndex(([[x, y]], index) => index === 2 || x * a + y * b > 1);
|
|
93
|
+
const [k0, k1, k2, k3, k4] = SATURATION_FIT[channel][1];
|
|
94
|
+
const weights = LMS_TO_LINEAR_SRGB[channel];
|
|
95
|
+
const saturation = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b;
|
|
96
|
+
const slopes = lmsSlopes(a, b);
|
|
97
|
+
const base = slopes.map((k) => 1 + saturation * k);
|
|
98
|
+
const dot = (values) => values.reduce((sum, value, index) => sum + weights[index] * value, 0);
|
|
99
|
+
const f = dot(base.map((value) => value ** 3));
|
|
100
|
+
const f1 = dot(base.map((value, index) => 3 * slopes[index] * value ** 2));
|
|
101
|
+
const f2 = dot(base.map((value, index) => 6 * slopes[index] ** 2 * value));
|
|
102
|
+
return saturation - (f * f1) / (f1 * f1 - 0.5 * f * f2);
|
|
103
|
+
}
|
|
104
|
+
/** Oklab lightness and chroma of the most saturated sRGB color of hue (a, b). */
|
|
105
|
+
function cusp(a, b) {
|
|
106
|
+
const saturation = maxSaturation(a, b);
|
|
107
|
+
const lightness = Math.cbrt(1 / Math.max(...oklabToLinearSrgb([1, saturation * a, saturation * b])));
|
|
108
|
+
return [lightness, lightness * saturation];
|
|
109
|
+
}
|
|
110
|
+
/** Chroma where the constant-lightness line at `lightness` leaves the sRGB gamut. */
|
|
111
|
+
function maxChroma(a, b, lightness, [cuspL, cuspC]) {
|
|
112
|
+
if (lightness <= cuspL)
|
|
113
|
+
return (cuspC * lightness) / cuspL;
|
|
114
|
+
// Upper half: triangle edge, then one Halley step against each channel reaching 1.
|
|
115
|
+
const t = (cuspC * (lightness - 1)) / (cuspL - 1);
|
|
116
|
+
const slopes = lmsSlopes(a, b);
|
|
117
|
+
const lms = slopes.map((k) => lightness + t * k);
|
|
118
|
+
const cubes = lms.map((value) => value ** 3);
|
|
119
|
+
const first = lms.map((value, index) => 3 * slopes[index] * value ** 2);
|
|
120
|
+
const second = lms.map((value, index) => 6 * slopes[index] ** 2 * value);
|
|
121
|
+
const dot = (row, values) => row[0] * values[0] + row[1] * values[1] + row[2] * values[2];
|
|
122
|
+
const steps = LMS_TO_LINEAR_SRGB.map((row) => {
|
|
123
|
+
const f = dot(row, cubes) - 1;
|
|
124
|
+
const f1 = dot(row, first);
|
|
125
|
+
const f2 = dot(row, second);
|
|
126
|
+
const u = f1 / (f1 * f1 - 0.5 * f * f2);
|
|
127
|
+
return u >= 0 ? -f * u : Number.MAX_VALUE;
|
|
128
|
+
});
|
|
129
|
+
return t + Math.min(...steps);
|
|
130
|
+
}
|
|
131
|
+
/** OKHSL's chroma reference points at lightness L and hue (a, b): [c0, cMid, cMax]. */
|
|
132
|
+
function chromaStops(L, a, b) {
|
|
133
|
+
const peak = cusp(a, b);
|
|
134
|
+
const cMax = maxChroma(a, b, L, peak);
|
|
135
|
+
const k = cMax / Math.min(L * (peak[1] / peak[0]), (1 - L) * (peak[1] / (1 - peak[0])));
|
|
136
|
+
const midS = 0.11516993 +
|
|
137
|
+
1 /
|
|
138
|
+
(7.4477897 +
|
|
139
|
+
4.1590124 * b +
|
|
140
|
+
a *
|
|
141
|
+
(-2.19557347 +
|
|
142
|
+
1.75198401 * b +
|
|
143
|
+
a * (-2.13704948 - 10.02301043 * b + a * (-4.24894561 + 5.38770819 * b + 4.69891013 * a))));
|
|
144
|
+
const midT = 0.11239642 +
|
|
145
|
+
1 /
|
|
146
|
+
(1.6132032 -
|
|
147
|
+
0.68124379 * b +
|
|
148
|
+
a *
|
|
149
|
+
(0.40370612 +
|
|
150
|
+
0.90148123 * b +
|
|
151
|
+
a * (-0.27087943 + 0.6122399 * b + a * (0.00299215 - 0.45399568 * b - 0.14661872 * a))));
|
|
152
|
+
const cMid = 0.9 * k * Math.sqrt(Math.sqrt(1 / (1 / (L * midS) ** 4 + 1 / ((1 - L) * midT) ** 4)));
|
|
153
|
+
const c0 = Math.sqrt(1 / (1 / (L * 0.4) ** 2 + 1 / ((1 - L) * 0.8) ** 2));
|
|
154
|
+
return [c0, cMid, cMax];
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Convert OKHSL to sRGB channels (0-255, rounded), clipping out-of-gamut channels.
|
|
158
|
+
* @param hue Hue in degrees.
|
|
159
|
+
* @param saturation Saturation, 0-1.
|
|
160
|
+
* @param lightness Lightness, 0-1.
|
|
161
|
+
*/
|
|
162
|
+
export function okhslToRgb(hue, saturation, lightness) {
|
|
163
|
+
const L = okhslToOklabLightness(lightness);
|
|
164
|
+
let lab = [L, 0, 0];
|
|
165
|
+
if (L > 0 && L < 1 && saturation > 0) {
|
|
166
|
+
const angle = (2 * Math.PI * (((hue % 360) + 360) % 360)) / 360;
|
|
167
|
+
const a = Math.cos(angle);
|
|
168
|
+
const b = Math.sin(angle);
|
|
169
|
+
const [c0, cMid, cMax] = chromaStops(L, a, b);
|
|
170
|
+
// Chroma rises from 0 through cMid at s = 0.8 to cMax at s = 1.
|
|
171
|
+
let chroma;
|
|
172
|
+
if (saturation < 0.8) {
|
|
173
|
+
const t = 1.25 * saturation;
|
|
174
|
+
const k1 = 0.8 * c0;
|
|
175
|
+
chroma = (t * k1) / (1 - (1 - k1 / cMid) * t);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const t = 5 * (saturation - 0.8);
|
|
179
|
+
const k1 = (0.2 * cMid ** 2 * 1.25 ** 2) / c0;
|
|
180
|
+
chroma = cMid + (t * k1) / (1 - (1 - k1 / (cMax - cMid)) * t);
|
|
181
|
+
}
|
|
182
|
+
lab = [L, chroma * a, chroma * b];
|
|
183
|
+
}
|
|
184
|
+
return linearSrgbToRgb(oklabToLinearSrgb(lab));
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Convert sRGB channels (0-255) to OKHSL.
|
|
188
|
+
* @returns Hue `h` in degrees (0 for grays), saturation `s` and lightness `l` 0-1.
|
|
189
|
+
*/
|
|
190
|
+
export function rgbToOkhsl(rgb) {
|
|
191
|
+
const [L, labA, labB] = rgbToOklab(rgb);
|
|
192
|
+
const chroma = Math.hypot(labA, labB);
|
|
193
|
+
const lightness = oklabToOkhslLightness(L);
|
|
194
|
+
if (chroma < 1e-9 || lightness <= 0 || lightness >= 1)
|
|
195
|
+
return { h: 0, s: 0, l: lightness };
|
|
196
|
+
const hue = ((Math.atan2(labB, labA) * 180) / Math.PI + 360) % 360;
|
|
197
|
+
const [c0, cMid, cMax] = chromaStops(L, labA / chroma, labB / chroma);
|
|
198
|
+
let saturation;
|
|
199
|
+
if (chroma < cMid) {
|
|
200
|
+
const k1 = 0.8 * c0;
|
|
201
|
+
saturation = 0.8 * (chroma / (k1 + (1 - k1 / cMid) * chroma));
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const k1 = (0.2 * cMid ** 2 * 1.25 ** 2) / c0;
|
|
205
|
+
const offset = chroma - cMid;
|
|
206
|
+
saturation = 0.8 + 0.2 * (offset / (k1 + (1 - k1 / (cMax - cMid)) * offset));
|
|
207
|
+
}
|
|
208
|
+
return { h: hue, s: Math.min(1, Math.max(0, saturation)), l: lightness };
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=oklab.js.map
|
|
@@ -9,7 +9,7 @@ export function detectTerminalCapabilities(env = process.env, platform = process
|
|
|
9
9
|
const termProgram = (env.TERM_PROGRAM ?? "").toLowerCase();
|
|
10
10
|
const terminalEmulator = env.TERMINAL_EMULATOR ?? "";
|
|
11
11
|
const colorTerm = env.COLORTERM ?? "";
|
|
12
|
-
const trueColor = colorTerm === "truecolor" || colorTerm === "24bit";
|
|
12
|
+
const trueColor = colorTerm === "truecolor" || colorTerm === "24bit" || term.toLowerCase().endsWith("-direct");
|
|
13
13
|
if (env.TMUX || term.startsWith("tmux")) {
|
|
14
14
|
const state = probeTmuxImageState(env, execTmux);
|
|
15
15
|
const override = parseTmuxKittyTerminalOverride(env.PI_TUI_TMUX_KITTY_TERMINAL);
|
|
@@ -4,7 +4,24 @@ export interface RgbColor {
|
|
|
4
4
|
b: number;
|
|
5
5
|
}
|
|
6
6
|
export type TerminalColorScheme = "dark" | "light";
|
|
7
|
-
|
|
8
|
-
export
|
|
7
|
+
/** Colors the terminal reports for its current theme. */
|
|
8
|
+
export interface TerminalColors {
|
|
9
|
+
/** Default foreground (OSC 10). */
|
|
10
|
+
foreground?: RgbColor;
|
|
11
|
+
/** Default background (OSC 11). */
|
|
12
|
+
background?: RgbColor;
|
|
13
|
+
/** ANSI colors 0-15 (OSC 4). Only set when the terminal reported all 16. */
|
|
14
|
+
palette?: RgbColor[];
|
|
15
|
+
}
|
|
16
|
+
/** What an OSC color reply reports: the default foreground (OSC 10), background (OSC 11), or a palette index (OSC 4). */
|
|
17
|
+
export type OscColorTarget = "foreground" | "background" | number;
|
|
18
|
+
/**
|
|
19
|
+
* Parse an OSC 10, 11, or 4 color reply. Returns undefined when `data` is not such a reply;
|
|
20
|
+
* `rgb` is undefined when it is a reply with an unparseable color.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseOscColorResponse(data: string): {
|
|
23
|
+
target: OscColorTarget;
|
|
24
|
+
rgb: RgbColor | undefined;
|
|
25
|
+
} | undefined;
|
|
9
26
|
export declare function parseTerminalColorSchemeReport(data: string): TerminalColorScheme | undefined;
|
|
10
27
|
//# sourceMappingURL=terminal-colors.d.ts.map
|
package/dist/terminal-colors.js
CHANGED
|
@@ -15,17 +15,22 @@ function parseOscHexChannel(channel) {
|
|
|
15
15
|
}
|
|
16
16
|
return Math.round((parseInt(channel, 16) / max) * 255);
|
|
17
17
|
}
|
|
18
|
-
const
|
|
18
|
+
const OSC_COLOR_RESPONSE_PATTERN = /^\x1b\](?:(1[01])|4;(\d{1,3}));([^\x07\x1b]*)(?:\x07|\x1b\\)$/i;
|
|
19
19
|
const COLOR_SCHEME_REPORT_PATTERN = /^(?:\x1b\[\?997;(1|2)n)+$/;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Parse an OSC 10, 11, or 4 color reply. Returns undefined when `data` is not such a reply;
|
|
22
|
+
* `rgb` is undefined when it is a reply with an unparseable color.
|
|
23
|
+
*/
|
|
24
|
+
export function parseOscColorResponse(data) {
|
|
25
|
+
const match = data.match(OSC_COLOR_RESPONSE_PATTERN);
|
|
25
26
|
if (!match) {
|
|
26
27
|
return undefined;
|
|
27
28
|
}
|
|
28
|
-
const
|
|
29
|
+
const target = match[1] === "10" ? "foreground" : match[1] === "11" ? "background" : Number.parseInt(match[2], 10);
|
|
30
|
+
return { target, rgb: parseOscColorValue(match[3]) };
|
|
31
|
+
}
|
|
32
|
+
function parseOscColorValue(rawValue) {
|
|
33
|
+
const value = rawValue.trim();
|
|
29
34
|
if (value.startsWith("#")) {
|
|
30
35
|
const hex = value.slice(1);
|
|
31
36
|
if (/^[0-9a-f]{6}$/i.test(hex)) {
|
package/dist/terminal-image.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TerminalColorMode } from "./colors.ts";
|
|
1
2
|
import { type DetectedTerminalCapabilities, type TerminalCapabilities } from "./terminal-capabilities.ts";
|
|
2
3
|
export type { ImageProtocol, TerminalCapabilities } from "./terminal-capabilities.ts";
|
|
3
4
|
export { outerKittyGraphicsMode } from "./terminal-capabilities.ts";
|
|
@@ -28,6 +29,7 @@ export declare function getCellDimensions(): CellDimensions;
|
|
|
28
29
|
export declare function setCellDimensions(dims: CellDimensions): void;
|
|
29
30
|
export declare function detectCapabilities(tmuxForwardsHyperlink?: () => boolean, tmuxPassthroughState?: () => TmuxPassthroughState): DetectedTerminalCapabilities;
|
|
30
31
|
export declare function getCapabilities(): TerminalCapabilities;
|
|
32
|
+
export declare function getTerminalColorMode(capabilities?: TerminalCapabilities): TerminalColorMode;
|
|
31
33
|
export declare function resetCapabilitiesCache(): void;
|
|
32
34
|
/** Override selected auto-detected capabilities. */
|
|
33
35
|
export declare function setCapabilityOverrides(overrides: Partial<TerminalCapabilities>): void;
|
|
@@ -109,7 +111,7 @@ export declare function getKittyImageMetadata(line: string): KittyImageMetadata
|
|
|
109
111
|
/** Build a placement-only command for an image line emitted by {@link renderImage}. */
|
|
110
112
|
export declare function getKittyImagePlacement(line: string): KittyImagePlacement | undefined;
|
|
111
113
|
export declare function cropKittyImageLine(line: string, hiddenRows: number, visibleRows: number): string;
|
|
112
|
-
export declare function calculateImageCellSize(imageDimensions: ImageDimensions, maxWidthCells: number, maxHeightCells?: number, cellDimensions?: CellDimensions): ImageCellSize;
|
|
114
|
+
export declare function calculateImageCellSize(imageDimensions: ImageDimensions, maxWidthCells: number, maxHeightCells?: number, cellDimensions?: CellDimensions, optimizeAspectRatio?: boolean): ImageCellSize;
|
|
113
115
|
export declare function getPngDimensions(base64Data: string): ImageDimensions | null;
|
|
114
116
|
export declare function getJpegDimensions(base64Data: string): ImageDimensions | null;
|
|
115
117
|
export declare function getGifDimensions(base64Data: string): ImageDimensions | null;
|
package/dist/terminal-image.js
CHANGED
|
@@ -35,7 +35,7 @@ function detectCapabilitiesFromEnvironment(tmuxForwardsHyperlink) {
|
|
|
35
35
|
const terminalEmulator = process.env.TERMINAL_EMULATOR?.toLowerCase() || "";
|
|
36
36
|
const term = process.env.TERM?.toLowerCase() || "";
|
|
37
37
|
const colorTerm = process.env.COLORTERM?.toLowerCase() || "";
|
|
38
|
-
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit";
|
|
38
|
+
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit" || term.endsWith("-direct");
|
|
39
39
|
const isWindowsConsole = process.platform === "win32";
|
|
40
40
|
// Emit OSC 8 hyperlinks only when tmux confirms it forwards.
|
|
41
41
|
// Image protocols are unreliable under tmux, so leave `images: null`.
|
|
@@ -132,6 +132,9 @@ export function getCapabilities() {
|
|
|
132
132
|
}
|
|
133
133
|
return cachedCapabilities;
|
|
134
134
|
}
|
|
135
|
+
export function getTerminalColorMode(capabilities = getCapabilities()) {
|
|
136
|
+
return capabilities.trueColor ? "truecolor" : "256color";
|
|
137
|
+
}
|
|
135
138
|
export function resetCapabilitiesCache() {
|
|
136
139
|
cachedCapabilities = null;
|
|
137
140
|
}
|
|
@@ -415,7 +418,15 @@ export function cropKittyImageLine(line, hiddenRows, visibleRows) {
|
|
|
415
418
|
controls.push(`y=${sourceY}`, `h=${sourceHeight}`, `r=${croppedRows}`);
|
|
416
419
|
return `${line.slice(0, match.index)}\x1b_G${controls.join(",")};${line.slice(match.index + match[0].length)}`;
|
|
417
420
|
}
|
|
418
|
-
|
|
421
|
+
function chooseLessDistortedCellCount(upperCount, idealCount) {
|
|
422
|
+
if (upperCount <= 1)
|
|
423
|
+
return upperCount;
|
|
424
|
+
const lowerCount = upperCount - 1;
|
|
425
|
+
const upperDistortion = Math.max(upperCount / idealCount, idealCount / upperCount);
|
|
426
|
+
const lowerDistortion = Math.max(lowerCount / idealCount, idealCount / lowerCount);
|
|
427
|
+
return lowerDistortion < upperDistortion ? lowerCount : upperCount;
|
|
428
|
+
}
|
|
429
|
+
export function calculateImageCellSize(imageDimensions, maxWidthCells, maxHeightCells, cellDimensions = { widthPx: 9, heightPx: 18 }, optimizeAspectRatio = false) {
|
|
419
430
|
const maxWidth = Math.max(1, Math.floor(maxWidthCells));
|
|
420
431
|
const maxHeight = maxHeightCells === undefined ? undefined : Math.max(1, Math.floor(maxHeightCells));
|
|
421
432
|
const imageWidth = Math.max(1, imageDimensions.widthPx);
|
|
@@ -425,12 +436,24 @@ export function calculateImageCellSize(imageDimensions, maxWidthCells, maxHeight
|
|
|
425
436
|
const scale = Math.min(widthScale, heightScale);
|
|
426
437
|
const scaledWidthPx = imageWidth * scale;
|
|
427
438
|
const scaledHeightPx = imageHeight * scale;
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
rows
|
|
433
|
-
}
|
|
439
|
+
let columns = Math.max(1, Math.min(maxWidth, Math.ceil(scaledWidthPx / cellDimensions.widthPx)));
|
|
440
|
+
const heightRows = scaledHeightPx / cellDimensions.heightPx;
|
|
441
|
+
let rows = Math.max(1, Math.ceil(heightRows));
|
|
442
|
+
if (maxHeight !== undefined) {
|
|
443
|
+
rows = Math.min(maxHeight, rows);
|
|
444
|
+
}
|
|
445
|
+
if (!optimizeAspectRatio) {
|
|
446
|
+
return { columns, rows };
|
|
447
|
+
}
|
|
448
|
+
if (widthScale <= heightScale) {
|
|
449
|
+
const idealRows = (columns * cellDimensions.widthPx * imageHeight) / (imageWidth * cellDimensions.heightPx);
|
|
450
|
+
rows = chooseLessDistortedCellCount(rows, idealRows);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
const idealColumns = (rows * cellDimensions.heightPx * imageWidth) / (imageHeight * cellDimensions.widthPx);
|
|
454
|
+
columns = chooseLessDistortedCellCount(columns, idealColumns);
|
|
455
|
+
}
|
|
456
|
+
return { columns, rows };
|
|
434
457
|
}
|
|
435
458
|
export function getPngDimensions(base64Data) {
|
|
436
459
|
try {
|
|
@@ -564,7 +587,8 @@ export function renderImage(base64Data, imageDimensions, options = {}) {
|
|
|
564
587
|
return null;
|
|
565
588
|
}
|
|
566
589
|
const maxWidth = options.maxWidthCells ?? 80;
|
|
567
|
-
|
|
590
|
+
// Reduce Kitty's cell-aligned distortion without shrinking iTerm2 reservations.
|
|
591
|
+
const size = calculateImageCellSize(imageDimensions, maxWidth, options.maxHeightCells, getCellDimensions(), caps.images === "kitty");
|
|
568
592
|
if (caps.images === "kitty" && caps.kittyUnicodePlaceholders) {
|
|
569
593
|
// Virtual placement: transmit once, then show the image with plain-text
|
|
570
594
|
// placeholder cells. Under tmux the placeholder cells move/clip with the
|
package/dist/terminal.d.ts
CHANGED
|
@@ -97,6 +97,8 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
97
97
|
private _kittyProtocolActive;
|
|
98
98
|
private _modifyOtherKeysActive;
|
|
99
99
|
private keyboardProtocolPushed;
|
|
100
|
+
/** DA1 replies owed to keyboard protocol queries. Later DA1 replies answer other queries and are forwarded. */
|
|
101
|
+
private pendingKeyboardProtocolDeviceAttributes;
|
|
100
102
|
private keyboardProtocolNegotiationBuffer;
|
|
101
103
|
private discardingPrivateResponse;
|
|
102
104
|
private keyboardProtocolBufferFlushTimer?;
|
|
@@ -143,6 +145,7 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
143
145
|
*/
|
|
144
146
|
private queryAndEnableKittyProtocol;
|
|
145
147
|
private handleKeyboardProtocolNegotiationSequence;
|
|
148
|
+
/** Returns the parsed negotiation reply with its full (possibly reassembled) sequence. */
|
|
146
149
|
private readKeyboardProtocolNegotiationSequence;
|
|
147
150
|
private setKeyboardProtocolNegotiationBuffer;
|
|
148
151
|
private clearKeyboardProtocolNegotiationBuffer;
|
package/dist/terminal.js
CHANGED
|
@@ -188,6 +188,8 @@ export class ProcessTerminal {
|
|
|
188
188
|
this._kittyProtocolActive = false;
|
|
189
189
|
this._modifyOtherKeysActive = false;
|
|
190
190
|
this.keyboardProtocolPushed = false;
|
|
191
|
+
/** DA1 replies owed to keyboard protocol queries. Later DA1 replies answer other queries and are forwarded. */
|
|
192
|
+
this.pendingKeyboardProtocolDeviceAttributes = 0;
|
|
191
193
|
this.keyboardProtocolNegotiationBuffer = "";
|
|
192
194
|
this.discardingPrivateResponse = false;
|
|
193
195
|
this.writeLogPath = (() => {
|
|
@@ -399,15 +401,15 @@ export class ProcessTerminal {
|
|
|
399
401
|
return;
|
|
400
402
|
}
|
|
401
403
|
}
|
|
402
|
-
const
|
|
403
|
-
if (
|
|
404
|
+
const negotiation = this.readKeyboardProtocolNegotiationSequence(sequence);
|
|
405
|
+
if (negotiation === "pending") {
|
|
404
406
|
this.scheduleKeyboardProtocolNegotiationBufferFlush();
|
|
405
407
|
return; // Wait briefly for the rest of a split Kitty response.
|
|
406
408
|
}
|
|
407
|
-
if (this.handleKeyboardProtocolNegotiationSequence(
|
|
409
|
+
if (negotiation && this.handleKeyboardProtocolNegotiationSequence(negotiation.parsed)) {
|
|
408
410
|
return;
|
|
409
411
|
}
|
|
410
|
-
this.forwardInputSequence(sequence);
|
|
412
|
+
this.forwardInputSequence(negotiation?.sequence ?? sequence);
|
|
411
413
|
});
|
|
412
414
|
// Re-wrap paste content with bracketed paste markers for existing editor handling
|
|
413
415
|
this.stdinBuffer.on("paste", (content) => {
|
|
@@ -443,12 +445,11 @@ export class ProcessTerminal {
|
|
|
443
445
|
this.enableModifyOtherKeys();
|
|
444
446
|
}
|
|
445
447
|
this.keyboardProtocolPushed = true;
|
|
448
|
+
this.pendingKeyboardProtocolDeviceAttributes += 1;
|
|
446
449
|
this.clearKeyboardProtocolNegotiationBuffer();
|
|
447
450
|
this.rawWrite(KITTY_KEYBOARD_PROTOCOL_QUERY);
|
|
448
451
|
}
|
|
449
452
|
handleKeyboardProtocolNegotiationSequence(negotiationSequence) {
|
|
450
|
-
if (!negotiationSequence)
|
|
451
|
-
return false;
|
|
452
453
|
this.clearKeyboardProtocolNegotiationBuffer();
|
|
453
454
|
if (negotiationSequence.type === "cursor-position") {
|
|
454
455
|
if (this.cursorQuery?.issued && this.cursorQuery.tmuxPane === undefined) {
|
|
@@ -457,6 +458,11 @@ export class ProcessTerminal {
|
|
|
457
458
|
}
|
|
458
459
|
return true;
|
|
459
460
|
}
|
|
461
|
+
if (negotiationSequence.type === "device-attributes") {
|
|
462
|
+
if (this.pendingKeyboardProtocolDeviceAttributes === 0)
|
|
463
|
+
return false;
|
|
464
|
+
this.pendingKeyboardProtocolDeviceAttributes -= 1;
|
|
465
|
+
}
|
|
460
466
|
this.keyboardNegotiationSettled = true;
|
|
461
467
|
this.issueCursorQuery();
|
|
462
468
|
if (negotiationSequence.type === "kitty-flags") {
|
|
@@ -477,13 +483,14 @@ export class ProcessTerminal {
|
|
|
477
483
|
}
|
|
478
484
|
return true;
|
|
479
485
|
}
|
|
486
|
+
/** Returns the parsed negotiation reply with its full (possibly reassembled) sequence. */
|
|
480
487
|
readKeyboardProtocolNegotiationSequence(sequence) {
|
|
481
488
|
if (this.keyboardProtocolNegotiationBuffer) {
|
|
482
489
|
const bufferedSequence = this.keyboardProtocolNegotiationBuffer + sequence;
|
|
483
490
|
const negotiationSequence = parseKeyboardProtocolNegotiationSequence(bufferedSequence);
|
|
484
491
|
if (negotiationSequence) {
|
|
485
492
|
this.clearKeyboardProtocolNegotiationBuffer();
|
|
486
|
-
return negotiationSequence;
|
|
493
|
+
return { parsed: negotiationSequence, sequence: bufferedSequence };
|
|
487
494
|
}
|
|
488
495
|
if (isKeyboardProtocolNegotiationSequencePrefix(bufferedSequence)) {
|
|
489
496
|
this.setKeyboardProtocolNegotiationBuffer(bufferedSequence);
|
|
@@ -493,7 +500,7 @@ export class ProcessTerminal {
|
|
|
493
500
|
}
|
|
494
501
|
const negotiationSequence = parseKeyboardProtocolNegotiationSequence(sequence);
|
|
495
502
|
if (negotiationSequence)
|
|
496
|
-
return negotiationSequence;
|
|
503
|
+
return { parsed: negotiationSequence, sequence };
|
|
497
504
|
if (isKeyboardProtocolNegotiationSequencePrefix(sequence)) {
|
|
498
505
|
this.setKeyboardProtocolNegotiationBuffer(sequence);
|
|
499
506
|
return "pending";
|
package/dist/tui-alt-screen.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type { Terminal } from "./terminal.ts";
|
|
2
2
|
import { type Component, TuiBase, type TuiStopOptions, VIEWPORT_TUI, type ViewportTUI } from "./tui.ts";
|
|
3
|
+
import { type WheelScrollLines } from "./wheel-scroll.ts";
|
|
3
4
|
export interface TuiAltScreenOptions {
|
|
4
|
-
/**
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Logical lines moved for each mouse-wheel event (default: 1). `"auto"` accelerates fast wheel
|
|
7
|
+
* spins on terminals that send one event per notch. Alt+wheel moves five times as far.
|
|
8
|
+
*/
|
|
9
|
+
wheelScrollLines?: WheelScrollLines;
|
|
6
10
|
/** Capture mouse events for viewport scrolling and application-owned text selection. */
|
|
7
11
|
mouse?: boolean;
|
|
8
12
|
/** Style a non-current transcript search match. */
|
|
@@ -23,10 +27,11 @@ export interface TuiAltScreenOptions {
|
|
|
23
27
|
/** Automatically copy selected text to the clipboard on mouse release (default: true). */
|
|
24
28
|
copyOnSelect?: boolean;
|
|
25
29
|
/**
|
|
26
|
-
* Copy selected text to the system clipboard. Return `true` on success
|
|
27
|
-
*
|
|
30
|
+
* Copy selected text to the system clipboard. Return `true` on success, an error message to
|
|
31
|
+
* display on failure, or `false` for a generic error. When omitted, the selection is copied
|
|
32
|
+
* via an OSC 52 write.
|
|
28
33
|
*/
|
|
29
|
-
copySelection?: (text: string) => Promise<boolean>;
|
|
34
|
+
copySelection?: (text: string) => Promise<boolean | string>;
|
|
30
35
|
}
|
|
31
36
|
/** Alternate-screen TUI with a scrollable, application-owned viewport. */
|
|
32
37
|
export declare class TuiAltScreen extends TuiBase implements ViewportTUI {
|
|
@@ -65,7 +70,7 @@ export declare class TuiAltScreen extends TuiBase implements ViewportTUI {
|
|
|
65
70
|
private mousePressPoint?;
|
|
66
71
|
private mousePressMoved;
|
|
67
72
|
private lastComponentClick?;
|
|
68
|
-
private readonly
|
|
73
|
+
private readonly wheelScroll;
|
|
69
74
|
private readonly mouseEnabled;
|
|
70
75
|
private readonly searchMatchStyle;
|
|
71
76
|
private readonly searchCurrentMatchStyle;
|
|
@@ -78,6 +83,7 @@ export declare class TuiAltScreen extends TuiBase implements ViewportTUI {
|
|
|
78
83
|
constructor(terminal: Terminal, showHardwareCursor?: boolean, logDirectory?: string, options?: TuiAltScreenOptions);
|
|
79
84
|
get viewportTop(): number;
|
|
80
85
|
get isFollowingOutput(): boolean;
|
|
86
|
+
setWheelScrollLines(lines: WheelScrollLines): void;
|
|
81
87
|
getCopyOnSelect(): boolean;
|
|
82
88
|
setCopyOnSelect(enabled: boolean): void;
|
|
83
89
|
/** Whether the fullscreen viewport has a non-empty active text selection. */
|
|
@@ -119,7 +125,6 @@ export declare class TuiAltScreen extends TuiBase implements ViewportTUI {
|
|
|
119
125
|
private clearTextSelection;
|
|
120
126
|
private handleMouseEvent;
|
|
121
127
|
private parseWheelEvent;
|
|
122
|
-
private getWheelScrollLines;
|
|
123
128
|
private routeWheel;
|
|
124
129
|
private parseSgrMouseEvent;
|
|
125
130
|
private handleRightClickPaste;
|