@sayknow-cli/tui 0.7.1 → 0.7.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/CHANGELOG.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [0.7.1] - 2026-09-29
5
+ ## [0.7.2] - 2026-09-29
6
+
7
+ ### Removed
8
+
9
+ - The launch-card-only `prepareSayoPlaceholderImages` API and its kitty Unicode-placeholder implementation. Composer pet image rendering remains.
6
10
 
7
11
  ## [0.7.0] - 2026-09-29
8
12
 
@@ -8,7 +8,6 @@ export * from "./components/input";
8
8
  export * from "./components/loader";
9
9
  export * from "./components/markdown";
10
10
  export * from "./components/sayknow-pet";
11
- export * from "./components/sayo-placeholder";
12
11
  export * from "./components/sayo-sprite";
13
12
  export * from "./components/secret-input";
14
13
  export * from "./components/select-list";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/tui",
4
- "version": "0.7.1",
4
+ "version": "0.7.2",
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",
@@ -38,8 +38,8 @@
38
38
  "fmt": "biome format --write ."
39
39
  },
40
40
  "dependencies": {
41
- "@sayknow-cli/natives": "0.7.1",
42
- "@sayknow-cli/utils": "0.7.1",
41
+ "@sayknow-cli/natives": "0.7.2",
42
+ "@sayknow-cli/utils": "0.7.2",
43
43
  "lru-cache": "11.3.6",
44
44
  "marked": "^18.0.3"
45
45
  },
package/src/index.ts CHANGED
@@ -12,7 +12,6 @@ export * from "./components/input";
12
12
  export * from "./components/loader";
13
13
  export * from "./components/markdown";
14
14
  export * from "./components/sayknow-pet";
15
- export * from "./components/sayo-placeholder";
16
15
  export * from "./components/sayo-sprite";
17
16
  export * from "./components/secret-input";
18
17
  export * from "./components/select-list";
@@ -1,23 +0,0 @@
1
- import { type SayoPose, type SayoTint } from "./sayo-sprite";
2
- /** Largest image this module can address in cells. */
3
- export declare const SAYO_PLACEHOLDER_MAX_CELLS: number;
4
- export interface SayoPlaceholderImages {
5
- columns: number;
6
- rows: number;
7
- /** Escape payload that uploads every pose and creates its virtual placement. */
8
- upload: string;
9
- /** Text row `row` of `pose`: placeholder cells colored with that pose's image id. */
10
- line(pose: SayoPose, row: number): string;
11
- }
12
- /**
13
- * Prepare `poses` of Sayo in `tint`, `rows` cells tall and square in pixels. Image ids
14
- * are `idBase + index` and must fit in 24 bits (the id travels as an RGB color).
15
- */
16
- export declare function prepareSayoPlaceholderImages(options: {
17
- tint: SayoTint;
18
- rows: number;
19
- cellWidthPx: number;
20
- cellHeightPx: number;
21
- poses: readonly SayoPose[];
22
- idBase: number;
23
- }): SayoPlaceholderImages;
@@ -1,102 +0,0 @@
1
- /**
2
- * Sayo in text lines, through kitty graphics Unicode placeholders (Ghostty, kitty).
3
- *
4
- * Each pose is uploaded once as an image with a virtual placement (`U=1`). A line then
5
- * shows it by writing placeholder cells (U+10EEEE plus row/column diacritics) in a
6
- * foreground color that encodes the image id. The image lives in ordinary text: it
7
- * scrolls, redraws and clears with the transcript, and switching poses is a color change
8
- * on those cells rather than a new image.
9
- */
10
- import { deflateSync } from "node:zlib";
11
- import { renderSayoPose, type SayoPose, type SayoTint } from "./sayo-sprite";
12
-
13
- const PLACEHOLDER = String.fromCodePoint(0x10eeee);
14
-
15
- /** Kitty's row/column diacritics (rowcolumn-diacritics.txt), enough for small images. */
16
- const DIACRITICS = [
17
- 0x0305, 0x030d, 0x030e, 0x0310, 0x0312, 0x033d, 0x033e, 0x033f, 0x0346, 0x034a, 0x034b, 0x034c, 0x0350, 0x0351,
18
- 0x0352, 0x0357, 0x035b, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d,
19
- 0x036e, 0x036f,
20
- ].map(code => String.fromCodePoint(code));
21
-
22
- /** Largest image this module can address in cells. */
23
- export const SAYO_PLACEHOLDER_MAX_CELLS = DIACRITICS.length;
24
-
25
- export interface SayoPlaceholderImages {
26
- columns: number;
27
- rows: number;
28
- /** Escape payload that uploads every pose and creates its virtual placement. */
29
- upload: string;
30
- /** Text row `row` of `pose`: placeholder cells colored with that pose's image id. */
31
- line(pose: SayoPose, row: number): string;
32
- }
33
-
34
- function kittyChunks(control: string, base64: string): string {
35
- const CHUNK = 4096;
36
- let out = "";
37
- for (let offset = 0, first = true; offset < base64.length || first; offset += CHUNK, first = false) {
38
- const chunk = base64.slice(offset, offset + CHUNK);
39
- const more = offset + CHUNK < base64.length ? 1 : 0;
40
- out += first ? `\x1b_G${control},m=${more};${chunk}\x1b\\` : `\x1b_Gm=${more};${chunk}\x1b\\`;
41
- }
42
- return out;
43
- }
44
-
45
- /**
46
- * Prepare `poses` of Sayo in `tint`, `rows` cells tall and square in pixels. Image ids
47
- * are `idBase + index` and must fit in 24 bits (the id travels as an RGB color).
48
- */
49
- export function prepareSayoPlaceholderImages(options: {
50
- tint: SayoTint;
51
- rows: number;
52
- cellWidthPx: number;
53
- cellHeightPx: number;
54
- poses: readonly SayoPose[];
55
- idBase: number;
56
- }): SayoPlaceholderImages {
57
- const rows = Math.max(1, Math.min(SAYO_PLACEHOLDER_MAX_CELLS, Math.round(options.rows)));
58
- const side = rows * options.cellHeightPx;
59
- const columns = Math.max(1, Math.min(SAYO_PLACEHOLDER_MAX_CELLS, Math.round(side / options.cellWidthPx)));
60
- const width = columns * options.cellWidthPx;
61
- const height = rows * options.cellHeightPx;
62
- const left = Math.max(0, Math.floor((width - side) / 2));
63
- const ids = new Map<SayoPose, number>();
64
- let upload = "";
65
- for (const [index, pose] of options.poses.entries()) {
66
- const id = (options.idBase + index) & 0xffffff;
67
- ids.set(pose, id);
68
- const sprite = renderSayoPose(pose, options.tint, Math.min(side, width));
69
- const rgba = new Uint8Array(width * height * 4);
70
- const top = height - sprite.height;
71
- for (let y = 0; y < sprite.height; y++) {
72
- rgba.set(
73
- sprite.data.subarray(y * sprite.width * 4, (y + 1) * sprite.width * 4),
74
- ((y + top) * width + left) * 4,
75
- );
76
- }
77
- const data = deflateSync(rgba).toString("base64");
78
- upload += `\x1b_Ga=d,d=I,i=${id},q=2\x1b\\`;
79
- upload += kittyChunks(`a=t,f=32,o=z,s=${width},v=${height},i=${id},q=2`, data);
80
- upload += `\x1b_Ga=p,U=1,i=${id},c=${columns},r=${rows},q=2\x1b\\`;
81
- }
82
- const rowCache = new Map<string, string>();
83
- return {
84
- columns,
85
- rows,
86
- upload,
87
- line(pose, row) {
88
- const id = ids.get(pose) ?? ids.values().next().value ?? options.idBase;
89
- const key = `${id}:${row}`;
90
- let text = rowCache.get(key);
91
- if (text === undefined) {
92
- const color = `\x1b[38;2;${(id >> 16) & 255};${(id >> 8) & 255};${id & 255}m`;
93
- let cells = "";
94
- for (let column = 0; column < columns; column++)
95
- cells += `${PLACEHOLDER}${DIACRITICS[row]}${DIACRITICS[column]}`;
96
- text = `${color}${cells}\x1b[39m`;
97
- rowCache.set(key, text);
98
- }
99
- return text;
100
- },
101
- };
102
- }