@gajae-code/tui 0.13.2 → 0.13.3
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 +14 -6
- package/dist/types/components/gajae-pet.d.ts +39 -21
- package/dist/types/components/ouroboros-pet.d.ts +23 -0
- package/dist/types/components/select-list.d.ts +3 -2
- package/package.json +3 -3
- package/src/components/gajae-pet.ts +284 -43
- package/src/components/ouroboros-pet-frames.json +488 -0
- package/src/components/ouroboros-pet.ts +150 -0
- package/src/components/select-list.ts +17 -11
- package/src/tui.ts +37 -29
package/CHANGELOG.md
CHANGED
|
@@ -2,16 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.13.3] - 2026-08-15
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Added the Ouroboros pet component with frame data, selector integration, and animation lifecycle shared with the existing Gajae Pet (#4468).
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Non-finite overlay geometry can no longer turn frame padding into an infinite allocating loop on the main thread; margins, positions, offsets, and minimum widths now fall back to bounded terminal-relative values (#4481).
|
|
12
|
+
- Kitty inline images are no longer deleted when live output moves their anchor above the viewport, so terminal-native scrollback keeps previously rendered images visible (#4424).
|
|
13
|
+
|
|
5
14
|
## [0.13.2] - 2026-08-13
|
|
6
15
|
|
|
7
16
|
### Fixed
|
|
8
17
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Escape presses immediately followed by
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- Apple Terminal.app now retains its default keyboard mode when it does not support the Kitty keyboard protocol, avoiding the modifyOtherKeys fallback that breaks Korean/Hangul IME composition.
|
|
18
|
+
- Fast double-Esc and triple-Esc sequences coalesced into one stdin chunk by tmux or SSH are now emitted as individual Escape presses, restoring draft-clear and selector gestures while preserving atomic Option-as-Meta sequences (#4312 by @Yeachan-Heo).
|
|
19
|
+
- Ambiguous trailing Escape bytes now remain buffered until a continuation or flush timeout resolves them, preventing a split `ESC ESC ESC [A` sequence from firing the destructive double-Escape gesture (#4312 by @Yeachan-Heo).
|
|
20
|
+
- Escape presses immediately followed by bracketed paste are now emitted individually instead of being swallowed as an unbound `alt+escape` sequence (#4312 by @Yeachan-Heo).
|
|
21
|
+
- Long runs of Escape bytes now decode in linear time without repeatedly rescanning accumulated input; 50,000 byte-by-byte reads now complete in milliseconds instead of seconds (#4312 by @Yeachan-Heo).
|
|
22
|
+
- Apple Terminal.app now retains its default keyboard mode when it does not support the Kitty keyboard protocol, avoiding the modifyOtherKeys fallback that broke Korean/Hangul IME composition (#4297 by @Yeachan-Heo).
|
|
15
23
|
|
|
16
24
|
## [0.13.1] - 2026-08-11
|
|
17
25
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { type OuroborosFrameName } from "./ouroboros-pet";
|
|
1
2
|
/**
|
|
2
3
|
* ┌─ GAJAE PET SPRITE SPEC ────────────────────────────────────────────────┐
|
|
3
|
-
*
|
|
4
|
-
* data: no PNGs
|
|
5
|
-
*
|
|
4
|
+
* Pets are square pixel sprites drawn beside the composer. Everything here is
|
|
5
|
+
* data: no PNGs or binary assets. Current pet frames are 16×16 and render into
|
|
6
|
+
* the same terminal footprint.
|
|
6
7
|
*
|
|
7
8
|
* GRID RULES
|
|
8
|
-
* -
|
|
9
|
+
* - Every frame within a skin has the same square dimensions.
|
|
9
10
|
* - `.` = transparent. Keep the outer columns transparent so the sprite sits
|
|
10
11
|
* snug beside the input box (the widget reserves +1 column of slack).
|
|
11
12
|
*
|
|
@@ -26,7 +27,8 @@
|
|
|
26
27
|
* RENDERING: buildGajaePixelFrames({ protocol, cellWidthPx, cellHeightPx,
|
|
27
28
|
* targetRows: 2 }) scales the art to 2 terminal rows and encodes each frame
|
|
28
29
|
* once. Kitty uses a native `Y=` sub-cell drop (set by the widget) to sit on the
|
|
29
|
-
* composer border; sixel uses transparent top padding
|
|
30
|
+
* composer border; sixel uses transparent top padding; iTerm2 uses an inline PNG
|
|
31
|
+
* sized to the reserved cell block.
|
|
30
32
|
*
|
|
31
33
|
* BEHAVIOR (timing, positioning, on/off) lives in
|
|
32
34
|
* packages/coding-agent/src/modes/components/gajae-pet-widget.ts.
|
|
@@ -34,26 +36,28 @@
|
|
|
34
36
|
* ADD A FRAME: draw the grid → add its name to GajaePixelFrameName → register it in
|
|
35
37
|
* PIXEL_GRIDS → reference it from an idle/work loop or a skin burst.
|
|
36
38
|
*
|
|
37
|
-
* ADD A PET (skin): append one
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* widget reads `burst` to animate — no other file needs editing. Recolor with a palette
|
|
41
|
-
* spread (see BLUE_PALETTE); add frames only for poses the catalog lacks.
|
|
39
|
+
* ADD A PET (skin): append one PET_SKINS entry with its palette, frame registry,
|
|
40
|
+
* base/idle/work animations and burst. The id flows into PetSkinId/PetMode
|
|
41
|
+
* automatically; settings, `/pet`, and both selectors derive from PET_SKINS.
|
|
42
42
|
* └────────────────────────────────────────────────────────────────────────┘
|
|
43
43
|
*/
|
|
44
44
|
type Rgb = readonly [number, number, number];
|
|
45
45
|
export type Palette = Record<string, Rgb | null>;
|
|
46
|
-
export declare const PET_SKIN_IDS: readonly ["red", "blue"];
|
|
46
|
+
export declare const PET_SKIN_IDS: readonly ["red", "blue", "ouroboros"];
|
|
47
47
|
export type PetSkinId = (typeof PET_SKIN_IDS)[number];
|
|
48
48
|
/** Every pet mode: "off" plus each skin id, in menu order. */
|
|
49
|
-
export declare const PET_MODE_IDS: readonly ["off", "red", "blue"];
|
|
49
|
+
export declare const PET_MODE_IDS: readonly ["off", "red", "blue", "ouroboros"];
|
|
50
50
|
export type PetMode = (typeof PET_MODE_IDS)[number];
|
|
51
51
|
/** Narrow an arbitrary string to a PetMode. */
|
|
52
52
|
export declare function isPetMode(value: string): value is PetMode;
|
|
53
|
+
/** Resolve a persisted mode after a skin has been removed. Explicit "off" remains off. */
|
|
54
|
+
export declare function resolvePetMode(value: string): PetMode;
|
|
53
55
|
/** Logical pixel-pet frame names shared by the overlay state machine. */
|
|
54
56
|
export type GajaePixelFrameName = "base" | "gazeL" | "gazeR" | "flicker" | "flex" | "danceL" | "danceR" | "cry1" | "cry2" | "cry3";
|
|
57
|
+
export type PetFrameName = GajaePixelFrameName | OuroborosFrameName;
|
|
55
58
|
/** Para-para work dance beats: the working loop and each skin's burst "work-in" intro. */
|
|
56
59
|
export declare const PARA_PARA_STEPS: ReadonlyArray<readonly [GajaePixelFrameName, number]>;
|
|
60
|
+
export declare const GAJAE_IDLE_STEPS: ReadonlyArray<readonly [GajaePixelFrameName, number]>;
|
|
57
61
|
/**
|
|
58
62
|
* A skin's idle burst: a short intro sequence, then an optional looping tail. It drives
|
|
59
63
|
* BOTH the random live show-off AND the selector's preview demo, so give every skin a
|
|
@@ -61,10 +65,10 @@ export declare const PARA_PARA_STEPS: ReadonlyArray<readonly [GajaePixelFrameNam
|
|
|
61
65
|
*/
|
|
62
66
|
export interface PetBurst {
|
|
63
67
|
/** Frames played once, in order, at the start of the burst. */
|
|
64
|
-
intro: ReadonlyArray<readonly [
|
|
68
|
+
intro: ReadonlyArray<readonly [PetFrameName, number]>;
|
|
65
69
|
/** Frames cycled every `stepMs` for `ms` after the intro (a held or looping finish). */
|
|
66
70
|
tail?: {
|
|
67
|
-
frames: readonly
|
|
71
|
+
frames: readonly PetFrameName[];
|
|
68
72
|
stepMs: number;
|
|
69
73
|
ms: number;
|
|
70
74
|
};
|
|
@@ -77,28 +81,38 @@ export interface PetSkin {
|
|
|
77
81
|
/** One-line selector/settings description. */
|
|
78
82
|
description: string;
|
|
79
83
|
palette: Palette;
|
|
84
|
+
frames: Readonly<Record<string, string[]>>;
|
|
85
|
+
baseFrame: PetFrameName;
|
|
86
|
+
idle: ReadonlyArray<readonly [PetFrameName, number]>;
|
|
87
|
+
workEnter?: ReadonlyArray<readonly [PetFrameName, number]>;
|
|
88
|
+
work: ReadonlyArray<readonly [PetFrameName, number]>;
|
|
89
|
+
workExit?: ReadonlyArray<readonly [PetFrameName, number]>;
|
|
80
90
|
/** Idle burst animation played between quiet idle loops. */
|
|
81
91
|
burst: PetBurst;
|
|
92
|
+
/** Optional variants that interrupt and then resume the work loop. */
|
|
93
|
+
workBursts?: readonly PetBurst[];
|
|
82
94
|
}
|
|
83
95
|
/** Skin registry — the single source for palettes, behavior and selector/command copy. */
|
|
84
96
|
export declare const PET_SKINS: Record<PetSkinId, PetSkin>;
|
|
85
97
|
/** Total burst duration (intro beats plus the looping tail). */
|
|
86
98
|
export declare function petBurstDurationMs(burst: PetBurst): number;
|
|
87
99
|
/** The frame to show `elapsed` ms into a burst (`now` cycles the looping tail). */
|
|
88
|
-
export declare function petBurstFrame(burst: PetBurst, elapsed: number, now: number):
|
|
100
|
+
export declare function petBurstFrame(burst: PetBurst, elapsed: number, now: number): PetFrameName;
|
|
89
101
|
/** Test-only access to logical art; production rendering still uses encoded frames. */
|
|
90
102
|
export declare const __gajaePetTestHooks: {
|
|
91
|
-
getPixelGrid(name:
|
|
103
|
+
getPixelGrid(name: PetFrameName, skin?: PetSkinId): string[];
|
|
92
104
|
};
|
|
93
105
|
/** Encode a grid as a transparent SIXEL image, optionally bottom-aligned by top padding. */
|
|
94
106
|
export declare function encodeGridSixel(grid: string[], scale: number, topPaddingPx?: number, palette?: Palette): string;
|
|
107
|
+
/** Encode a grid as an iTerm2 inline PNG. */
|
|
108
|
+
export declare function encodeGridIterm2(grid: string[], scale: number, topPaddingPx?: number, bottomPaddingPx?: number, palette?: Palette): string;
|
|
95
109
|
/** Encode a bottom-aligned grid as kitty raw RGBA at `scale`. */
|
|
96
110
|
export declare function encodeGridKitty(grid: string[], scale: number, imageId: number, cols: number, rows: number, topPaddingPx?: number, cellYOffsetPx?: number, leftPaddingPx?: number, rightPaddingPx?: number, palette?: Palette): string;
|
|
97
111
|
export interface GajaePixelFrames {
|
|
98
112
|
/** escape payload per logical frame (drawn at the current cursor cell) */
|
|
99
|
-
frames: Record<
|
|
113
|
+
frames: Record<string, string>;
|
|
100
114
|
/** protocol the frames were encoded for */
|
|
101
|
-
protocol: "sixel" | "kitty";
|
|
115
|
+
protocol: "sixel" | "kitty" | "iterm2";
|
|
102
116
|
widthPx: number;
|
|
103
117
|
heightPx: number;
|
|
104
118
|
columns: number;
|
|
@@ -108,11 +122,11 @@ export interface GajaePixelFrames {
|
|
|
108
122
|
}
|
|
109
123
|
/**
|
|
110
124
|
* Build overlay pixel frames exactly `targetRows` terminal rows tall when the
|
|
111
|
-
* terminal cells permit it.
|
|
112
|
-
*
|
|
125
|
+
* terminal cells permit it. Each skin owns its source resolution so future
|
|
126
|
+
* additions can opt into denser art without changing the terminal footprint.
|
|
113
127
|
*/
|
|
114
128
|
export declare function buildGajaePixelFrames(options: {
|
|
115
|
-
protocol: "sixel" | "kitty";
|
|
129
|
+
protocol: "sixel" | "kitty" | "iterm2";
|
|
116
130
|
cellWidthPx: number;
|
|
117
131
|
cellHeightPx: number;
|
|
118
132
|
targetRows?: number;
|
|
@@ -121,6 +135,10 @@ export declare function buildGajaePixelFrames(options: {
|
|
|
121
135
|
/** Native sub-cell `Y=` pixel offset that drops the kitty sprite within its first cell. */
|
|
122
136
|
kittyCellYOffsetPx?: number;
|
|
123
137
|
kittyImageId?: number;
|
|
138
|
+
/** Transparent iTerm2-only top padding for half-cell vertical alignment. */
|
|
139
|
+
iterm2TopPaddingPx?: number;
|
|
140
|
+
/** Transparent iTerm2-only bottom padding inside the two-row canvas. */
|
|
141
|
+
iterm2BottomPaddingPx?: number;
|
|
124
142
|
/** Color skin for the sprite palette (default "red"). */
|
|
125
143
|
skin?: PetSkinId;
|
|
126
144
|
}): GajaePixelFrames;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const OUROBOROS_FRAME_NAMES: readonly ["idle", "tongue-1", "tongue-2", "blink", "cry-1", "cry-2", "cry-3", "heart-turn-0", "heart-turn-1", "heart-turn-3", "heart-turn-4", "heart-turn-5", "heart-turn-6", "heart-turn-10", "heart-turn-11", "heart", "heart-accent", "enter-1", "enter-2", "spin-1", "spin-2", "spin-3", "spin-4", "spin-5", "spin-6", "spin-7", "spin-8"];
|
|
2
|
+
export type OuroborosFrameName = (typeof OUROBOROS_FRAME_NAMES)[number];
|
|
3
|
+
export declare const OUROBOROS_PIXEL_GRIDS: Record<OuroborosFrameName, string[]>;
|
|
4
|
+
/** Quiet loop with frequent tongue flicks and an occasional three-drop sob. */
|
|
5
|
+
export declare const OUROBOROS_IDLE_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
6
|
+
/** One sob builds into three distinct tear drops: 1-2-3-2-3-2-3. */
|
|
7
|
+
export declare const OUROBOROS_CRY_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
8
|
+
/**
|
|
9
|
+
* Signature flex: the eye opens before the body rolls counterclockwise through
|
|
10
|
+
* 45°, 90°, 120°, and the final overlap with the heart pose. The return reuses
|
|
11
|
+
* the authored frames in reverse.
|
|
12
|
+
*/
|
|
13
|
+
export declare const OUROBOROS_HEART_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
14
|
+
/** Head leads left while the resting coil resolves into the infinity loop. */
|
|
15
|
+
export declare const OUROBOROS_WORK_ENTER_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
16
|
+
/** The exact inverse of work entry, so stopping visibly unwinds into idle. */
|
|
17
|
+
export declare const OUROBOROS_WORK_EXIT_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
18
|
+
/** Retouched eight-frame infinity loop with the full body following the head. */
|
|
19
|
+
export declare const OUROBOROS_WORK_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
20
|
+
/** Leave the infinity loop, perform the heart flourish, then resolve back into it. */
|
|
21
|
+
export declare const OUROBOROS_WORK_HEART_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
22
|
+
/** Leave the infinity loop for a three-drop sob, then resume at full size. */
|
|
23
|
+
export declare const OUROBOROS_WORK_CRY_STEPS: ReadonlyArray<readonly [OuroborosFrameName, number]>;
|
|
@@ -21,6 +21,7 @@ export interface SelectListTheme {
|
|
|
21
21
|
noMatch: (text: string) => string;
|
|
22
22
|
symbols: SymbolTheme;
|
|
23
23
|
}
|
|
24
|
+
export type SelectListThemeSource = SelectListTheme | (() => SelectListTheme);
|
|
24
25
|
export interface SelectListTruncatePrimaryContext {
|
|
25
26
|
text: string;
|
|
26
27
|
maxWidth: number;
|
|
@@ -37,12 +38,12 @@ export declare class SelectList implements Component {
|
|
|
37
38
|
#private;
|
|
38
39
|
private readonly items;
|
|
39
40
|
private readonly maxVisible;
|
|
40
|
-
private readonly
|
|
41
|
+
private readonly themeSource;
|
|
41
42
|
private readonly layout;
|
|
42
43
|
onSelect?: (item: SelectItem) => void;
|
|
43
44
|
onCancel?: () => void;
|
|
44
45
|
onSelectionChange?: (item: SelectItem) => void;
|
|
45
|
-
constructor(items: ReadonlyArray<SelectItem>, maxVisible: number,
|
|
46
|
+
constructor(items: ReadonlyArray<SelectItem>, maxVisible: number, themeSource: SelectListThemeSource, layout?: SelectListLayoutOptions);
|
|
46
47
|
setFilter(filter: string): void;
|
|
47
48
|
setSelectedIndex(index: number): void;
|
|
48
49
|
handleNavigation(action: "tui.select.up" | "tui.select.down" | "tui.select.pageUp" | "tui.select.pageDown"): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/tui",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.3",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"fmt": "biome format --write ."
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@gajae-code/natives": "0.13.
|
|
40
|
-
"@gajae-code/utils": "0.13.
|
|
39
|
+
"@gajae-code/natives": "0.13.3",
|
|
40
|
+
"@gajae-code/utils": "0.13.3",
|
|
41
41
|
"lru-cache": "11.3.6",
|
|
42
42
|
"marked": "18.0.6"
|
|
43
43
|
},
|