@sayknow-cli/tui 0.3.11 → 0.3.13
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/package.json +8 -9
- package/src/components/image.ts +79 -39
- package/src/components/markdown.ts +282 -37
- package/src/components/sayknow-pet.ts +435 -0
- package/src/components/text.ts +60 -36
- package/src/index.ts +1 -0
- package/src/terminal-capabilities.ts +42 -0
- package/src/tui.ts +471 -55
- package/src/utils.ts +144 -8
- package/dist/types/animation-scheduler.d.ts +0 -13
- package/dist/types/autocomplete.d.ts +0 -83
- package/dist/types/bracketed-paste.d.ts +0 -26
- package/dist/types/components/box.d.ts +0 -20
- package/dist/types/components/cancellable-loader.d.ts +0 -21
- package/dist/types/components/editor.d.ts +0 -126
- package/dist/types/components/image.d.ts +0 -16
- package/dist/types/components/input.d.ts +0 -16
- package/dist/types/components/loader.d.ts +0 -23
- package/dist/types/components/markdown.d.ts +0 -77
- package/dist/types/components/select-list.d.ts +0 -46
- package/dist/types/components/settings-list.d.ts +0 -39
- package/dist/types/components/spacer.d.ts +0 -11
- package/dist/types/components/tab-bar.d.ts +0 -56
- package/dist/types/components/text.d.ts +0 -13
- package/dist/types/components/truncated-text.d.ts +0 -10
- package/dist/types/editor-component.d.ts +0 -36
- package/dist/types/fuzzy.d.ts +0 -15
- package/dist/types/index.d.ts +0 -27
- package/dist/types/keybindings.d.ts +0 -201
- package/dist/types/keys.d.ts +0 -208
- package/dist/types/kill-ring.d.ts +0 -27
- package/dist/types/metrics.d.ts +0 -85
- package/dist/types/stdin-buffer.d.ts +0 -50
- package/dist/types/symbols.d.ts +0 -23
- package/dist/types/terminal-capabilities.d.ts +0 -143
- package/dist/types/terminal.d.ts +0 -90
- package/dist/types/ttyid.d.ts +0 -9
- package/dist/types/tui.d.ts +0 -215
- package/dist/types/utils.d.ts +0 -87
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@sayknow-cli/tui",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.13",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://sayknow-cli.com",
|
|
7
7
|
"author": "jaybeyond",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"cli"
|
|
28
28
|
],
|
|
29
29
|
"main": "./src/index.ts",
|
|
30
|
-
"types": "./
|
|
30
|
+
"types": "./src/index.ts",
|
|
31
31
|
"scripts": {
|
|
32
32
|
"check": "biome check . && bun run check:types",
|
|
33
33
|
"check:types": "tsgo -p tsconfig.json --noEmit",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"fmt": "biome format --write ."
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@sayknow-cli/natives": "0.3.
|
|
42
|
-
"@sayknow-cli/utils": "0.3.
|
|
41
|
+
"@sayknow-cli/natives": "0.3.13",
|
|
42
|
+
"@sayknow-cli/utils": "0.3.13",
|
|
43
43
|
"lru-cache": "11.3.6",
|
|
44
44
|
"marked": "^18.0.3"
|
|
45
45
|
},
|
|
@@ -53,20 +53,19 @@
|
|
|
53
53
|
"files": [
|
|
54
54
|
"src",
|
|
55
55
|
"README.md",
|
|
56
|
-
"CHANGELOG.md"
|
|
57
|
-
"dist/types"
|
|
56
|
+
"CHANGELOG.md"
|
|
58
57
|
],
|
|
59
58
|
"exports": {
|
|
60
59
|
".": {
|
|
61
|
-
"types": "./
|
|
60
|
+
"types": "./src/index.ts",
|
|
62
61
|
"import": "./src/index.ts"
|
|
63
62
|
},
|
|
64
63
|
"./*": {
|
|
65
|
-
"types": "./
|
|
64
|
+
"types": "./src/*.ts",
|
|
66
65
|
"import": "./src/*.ts"
|
|
67
66
|
},
|
|
68
67
|
"./components/*": {
|
|
69
|
-
"types": "./
|
|
68
|
+
"types": "./src/components/*.ts",
|
|
70
69
|
"import": "./src/components/*.ts"
|
|
71
70
|
},
|
|
72
71
|
"./*.js": "./src/*.ts"
|
package/src/components/image.ts
CHANGED
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
type ImageDimensions,
|
|
4
4
|
ImageProtocol,
|
|
5
5
|
imageFallback,
|
|
6
|
+
isCursorNeutralImagePermittedInFallback,
|
|
7
|
+
isTerminalGraphicsFallbackActive,
|
|
6
8
|
kittyImageId,
|
|
7
9
|
renderImage,
|
|
8
10
|
TERMINAL,
|
|
@@ -28,10 +30,11 @@ export interface ImageOptions {
|
|
|
28
30
|
maxWidthCells?: number;
|
|
29
31
|
maxHeightCells?: number;
|
|
30
32
|
filename?: string;
|
|
33
|
+
refetch?: () => string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export class Image implements Component {
|
|
34
|
-
#base64Data
|
|
37
|
+
#base64Data?: string;
|
|
35
38
|
#mimeType: string;
|
|
36
39
|
#dimensions: ImageDimensions;
|
|
37
40
|
#theme: ImageTheme;
|
|
@@ -39,7 +42,8 @@ export class Image implements Component {
|
|
|
39
42
|
|
|
40
43
|
#cachedLines?: string[];
|
|
41
44
|
#cachedWidth?: number;
|
|
42
|
-
|
|
45
|
+
#cachedFallbackActive?: boolean;
|
|
46
|
+
#cachedProtocol?: ImageProtocol | null;
|
|
43
47
|
// Computed lazily so non-kitty terminals never pay the hash cost.
|
|
44
48
|
#kittyImageId?: number;
|
|
45
49
|
readonly #kittyPlacementId = allocatePlacementId();
|
|
@@ -61,10 +65,40 @@ export class Image implements Component {
|
|
|
61
65
|
invalidate(): void {
|
|
62
66
|
this.#cachedLines = undefined;
|
|
63
67
|
this.#cachedWidth = undefined;
|
|
68
|
+
this.#cachedFallbackActive = undefined;
|
|
69
|
+
this.#cachedProtocol = undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get retainedBase64DataForTest(): string | undefined {
|
|
73
|
+
return this.#base64Data;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#fallbackLines(): string[] {
|
|
77
|
+
const fallback = imageFallback(this.#mimeType, this.#dimensions, this.#options.filename);
|
|
78
|
+
return [this.#theme.fallbackColor(fallback)];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#getBase64Data(): string | undefined {
|
|
82
|
+
if (this.#base64Data) return this.#base64Data;
|
|
83
|
+
const refetched = this.#options.refetch?.();
|
|
84
|
+
if (refetched) this.#base64Data = refetched;
|
|
85
|
+
return this.#base64Data;
|
|
64
86
|
}
|
|
65
87
|
|
|
66
88
|
render(width: number): string[] {
|
|
67
|
-
|
|
89
|
+
// Kitty placements are cursor-neutral, so an opted-in fallback scope
|
|
90
|
+
// (e.g. the IRC split) can still render them safely; iTerm2/SIXEL
|
|
91
|
+
// advance the cursor and stay suppressed.
|
|
92
|
+
const graphicsSuppressed =
|
|
93
|
+
isTerminalGraphicsFallbackActive() &&
|
|
94
|
+
!(TERMINAL.imageProtocol === ImageProtocol.Kitty && isCursorNeutralImagePermittedInFallback());
|
|
95
|
+
const protocol = TERMINAL.imageProtocol;
|
|
96
|
+
if (
|
|
97
|
+
this.#cachedLines &&
|
|
98
|
+
this.#cachedWidth === width &&
|
|
99
|
+
this.#cachedFallbackActive === graphicsSuppressed &&
|
|
100
|
+
this.#cachedProtocol === protocol
|
|
101
|
+
) {
|
|
68
102
|
return this.#cachedLines;
|
|
69
103
|
}
|
|
70
104
|
|
|
@@ -73,51 +107,57 @@ export class Image implements Component {
|
|
|
73
107
|
|
|
74
108
|
let lines: string[];
|
|
75
109
|
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
110
|
+
if (protocol && !graphicsSuppressed) {
|
|
111
|
+
const base64Data = this.#getBase64Data();
|
|
112
|
+
if (!base64Data) {
|
|
113
|
+
lines = this.#fallbackLines();
|
|
114
|
+
} else {
|
|
115
|
+
if (protocol === ImageProtocol.Kitty) {
|
|
116
|
+
this.#kittyImageId ??= kittyImageId(base64Data);
|
|
117
|
+
}
|
|
118
|
+
const result = renderImage(base64Data, this.#dimensions, {
|
|
119
|
+
maxWidthCells: maxWidth,
|
|
120
|
+
maxHeightCells: this.#options.maxHeightCells,
|
|
121
|
+
imageId: this.#kittyImageId,
|
|
122
|
+
placementId: this.#kittyPlacementId,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (result) {
|
|
126
|
+
// Return `rows` lines so the TUI accounts for the image height.
|
|
127
|
+
if (result.cursorNeutral) {
|
|
128
|
+
// Kitty a=p,C=1 placements neither move the cursor nor carry
|
|
129
|
+
// pixel data, so the escape lives on the FIRST row — the image
|
|
130
|
+
// anchors to that cell and no cursor-up trick is needed (the
|
|
131
|
+
// old CUU approach clamped at the viewport top edge and placed
|
|
132
|
+
// the image over transcript text when partially scrolled out).
|
|
133
|
+
lines = [result.sequence];
|
|
134
|
+
for (let i = 0; i < result.rows - 1; i++) {
|
|
135
|
+
lines.push("");
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// iTerm2/SIXEL draw at the cursor and advance it: reserve
|
|
139
|
+
// rows-1 blank lines (TUI clears them), then move the cursor
|
|
140
|
+
// up and draw from the last line.
|
|
141
|
+
lines = [];
|
|
142
|
+
for (let i = 0; i < result.rows - 1; i++) {
|
|
143
|
+
lines.push("");
|
|
144
|
+
}
|
|
145
|
+
const moveUp = result.rows > 1 ? `\x1b[${result.rows - 1}A` : "";
|
|
146
|
+
lines.push(moveUp + result.sequence);
|
|
98
147
|
}
|
|
148
|
+
this.#base64Data = undefined;
|
|
99
149
|
} else {
|
|
100
|
-
|
|
101
|
-
// rows-1 blank lines (TUI clears them), then move the cursor
|
|
102
|
-
// up and draw from the last line.
|
|
103
|
-
lines = [];
|
|
104
|
-
for (let i = 0; i < result.rows - 1; i++) {
|
|
105
|
-
lines.push("");
|
|
106
|
-
}
|
|
107
|
-
const moveUp = result.rows > 1 ? `\x1b[${result.rows - 1}A` : "";
|
|
108
|
-
lines.push(moveUp + result.sequence);
|
|
150
|
+
lines = this.#fallbackLines();
|
|
109
151
|
}
|
|
110
|
-
} else {
|
|
111
|
-
const fallback = imageFallback(this.#mimeType, this.#dimensions, this.#options.filename);
|
|
112
|
-
lines = [this.#theme.fallbackColor(fallback)];
|
|
113
152
|
}
|
|
114
153
|
} else {
|
|
115
|
-
|
|
116
|
-
lines = [this.#theme.fallbackColor(fallback)];
|
|
154
|
+
lines = this.#fallbackLines();
|
|
117
155
|
}
|
|
118
156
|
|
|
119
157
|
this.#cachedLines = lines;
|
|
120
158
|
this.#cachedWidth = width;
|
|
159
|
+
this.#cachedFallbackActive = graphicsSuppressed;
|
|
160
|
+
this.#cachedProtocol = protocol;
|
|
121
161
|
|
|
122
162
|
return lines;
|
|
123
163
|
}
|