@code-yeongyu/senpi-tui 2026.11.3-1 → 2026.11.3-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/dist/index.d.ts CHANGED
@@ -34,6 +34,7 @@ export { parseTerminalColorSchemeReport, type RgbColor, type TerminalColorScheme
34
34
  export declare function calculateImageRows(imageDimensions: ImageDimensions, targetWidthCells: number, cellDimensions?: CellDimensions): number;
35
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";
36
36
  export { sanitizeTerminalLabel, shortenImagePath } from "./terminal-text.ts";
37
+ export { encodeProgramStatus, type ProgramStatus, type ProgramStatusKind, type ProgramStatusState, } from "./program-status.ts";
37
38
  export { type Component, CompositeRevision, Container, CURSOR_MARKER, claimFrameRow, compositeTuiLine, currentRenderRevision, dispatchMouseEvent, type Focusable, type FrameLineBytesTotals, frameLineBytesTotals, frameMode, frameScrollbackRows, isFocusable, isViewportTUI, joinLineArrays, mainScreenHistoryLines, nextRenderRevision, type OverlayAnchor, type OverlayBounds, type OverlayHandle, type OverlayMargin, type OverlayOptions, type OverlayUnfocusOptions, renderAtFrameRow, resetMainScreenHistoryLines, type SizeValue, TUI, type TuiInputListener, type TuiInputListenerResult, type TuiMode, type TuiMouseButton, type TuiMouseDispatchResult, type TuiMouseEvent, type TuiMouseEventResult, type TuiMouseEventType, type TuiStopOptions, type ViewportTUI, } from "./tui.ts";
38
39
  export { TuiAltScreen, type TuiAltScreenOptions } from "./tui-alt-screen.ts";
39
40
  export { TuiMainScreen, type TuiMainScreenRenderState } from "./tui-main-screen.ts";
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ export function calculateImageRows(imageDimensions, targetWidthCells, cellDimens
49
49
  }
50
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";
51
51
  export { sanitizeTerminalLabel, shortenImagePath } from "./terminal-text.js";
52
+ export { encodeProgramStatus, } from "./program-status.js";
52
53
  export { CompositeRevision, Container, CURSOR_MARKER, claimFrameRow, compositeTuiLine, currentRenderRevision, dispatchMouseEvent, frameLineBytesTotals, frameMode, frameScrollbackRows, isFocusable, isViewportTUI, joinLineArrays, mainScreenHistoryLines, nextRenderRevision, renderAtFrameRow, resetMainScreenHistoryLines, TUI, } from "./tui.js";
53
54
  export { TuiAltScreen } from "./tui-alt-screen.js";
54
55
  export { TuiMainScreen } from "./tui-main-screen.js";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * OSC 7501 Program Status Protocol (rev 0.4): a program tells its terminal whether it is idle, working,
3
+ * blocked on the user, done or failed. https://control-codes.page/proposals/osc-7501/
4
+ */
5
+ export type ProgramStatusState = "idle" | "working" | "blocked" | "done" | "error";
6
+ export type ProgramStatusKind = "permission" | "question" | "auth";
7
+ export interface ProgramStatus {
8
+ state: ProgramStatusState;
9
+ /** How the user must act; only sent with `blocked`. */
10
+ kind?: ProgramStatusKind;
11
+ /** One line on what the program is doing or waiting for. */
12
+ message?: string;
13
+ }
14
+ /**
15
+ * Build the OSC 7501 report for `status`, or the `state=clear` report that removes every record when it is
16
+ * undefined. `app` is repeated in every report because each one replaces its record; an app name outside the
17
+ * protocol's charset or length is left out rather than sent for the terminal to discard.
18
+ */
19
+ export declare function encodeProgramStatus(status: ProgramStatus | undefined, app?: string): string;
20
+ //# sourceMappingURL=program-status.d.ts.map
@@ -0,0 +1,45 @@
1
+ /**
2
+ * OSC 7501 Program Status Protocol (rev 0.4): a program tells its terminal whether it is idle, working,
3
+ * blocked on the user, done or failed. https://control-codes.page/proposals/osc-7501/
4
+ */
5
+ const MAX_MESSAGE_BYTES = 2048;
6
+ const APP_PATTERN = /^[A-Za-z0-9_.+-]{1,32}$/;
7
+ // C0, DEL and C1 controls: a decoded msg holding any of them makes the terminal drop the whole report.
8
+ const CONTROL_PATTERN = /[\u0000-\u001f\u007f-\u009f]+/g;
9
+ // Bidi overrides/isolates and invisible formatting, which a terminal may show outside the grid.
10
+ const INVISIBLE_FORMATTING_PATTERN = /[\u00ad\u061c\u180e\u200b\u200e\u200f\u202a-\u202e\u2060-\u2064\u2066-\u2069\ufeff]/g;
11
+ function sanitizeMessage(message) {
12
+ return message.replace(INVISIBLE_FORMATTING_PATTERN, "").replace(CONTROL_PATTERN, " ").replace(/\s+/g, " ").trim();
13
+ }
14
+ function truncateUtf8(text, maxBytes) {
15
+ if (Buffer.byteLength(text, "utf8") <= maxBytes)
16
+ return text;
17
+ let bytes = 0;
18
+ let result = "";
19
+ for (const char of text) {
20
+ bytes += Buffer.byteLength(char, "utf8");
21
+ if (bytes > maxBytes)
22
+ break;
23
+ result += char;
24
+ }
25
+ return result;
26
+ }
27
+ /**
28
+ * Build the OSC 7501 report for `status`, or the `state=clear` report that removes every record when it is
29
+ * undefined. `app` is repeated in every report because each one replaces its record; an app name outside the
30
+ * protocol's charset or length is left out rather than sent for the terminal to discard.
31
+ */
32
+ export function encodeProgramStatus(status, app) {
33
+ if (status === undefined)
34
+ return "\x1b]7501;state=clear\x1b\\";
35
+ const pairs = [`state=${status.state}`];
36
+ if (status.state === "blocked" && status.kind !== undefined)
37
+ pairs.push(`kind=${status.kind}`);
38
+ if (app !== undefined && APP_PATTERN.test(app))
39
+ pairs.push(`app=${app}`);
40
+ const message = status.message === undefined ? "" : truncateUtf8(sanitizeMessage(status.message), MAX_MESSAGE_BYTES);
41
+ if (message.length > 0)
42
+ pairs.push(`msg=${Buffer.from(message, "utf8").toString("base64")}`);
43
+ return `\x1b]7501;${pairs.join(":")}\x1b\\`;
44
+ }
45
+ //# sourceMappingURL=program-status.js.map
@@ -22,6 +22,10 @@ export declare function isAppleTerminalSession(): boolean;
22
22
  */
23
23
  export declare function refreshTerminalDimensions(): void;
24
24
  export declare function normalizeNativeShiftEnterInput(data: string, shouldDetectNativeShiftEnter: boolean, isShiftPressed: boolean): string;
25
+ /**
26
+ * Terminal.app can deliver Shift+Return as ESC CR, which legacy key parsing reads as Alt+Enter.
27
+ * The pressed native Shift keeps it apart from an Option-as-Meta Return, which stays Alt+Enter.
28
+ */
25
29
  export declare function normalizeAppleTerminalInput(data: string, isAppleTerminal: boolean, isShiftPressed: boolean): string;
26
30
  export declare function isWarpWslSession(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, socketExists?: (socketPath: string) => boolean): boolean;
27
31
  export declare function normalizeWarpWslShiftEnterInput(data: string, env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, socketExists?: (socketPath: string) => boolean): string;
package/dist/terminal.js CHANGED
@@ -12,6 +12,7 @@ const TERMINAL_PROGRESS_KEEPALIVE_MS = 1000;
12
12
  const TERMINAL_PROGRESS_ACTIVE_SEQUENCE = "\x1b]9;4;3\x07";
13
13
  const TERMINAL_PROGRESS_CLEAR_SEQUENCE = "\x1b]9;4;0\x07";
14
14
  const NATIVE_SHIFT_ENTER_SEQUENCE = "\x1b[13;2u";
15
+ const APPLE_TERMINAL_ESCAPED_RETURN_SEQUENCE = "\x1b\r";
15
16
  const DESIRED_KITTY_KEYBOARD_PROTOCOL_FLAGS = 7;
16
17
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
17
18
  // Bun's macOS tty shim can report synchronous ioctl EIO as raw positive errno 5 without a string code.
@@ -75,7 +76,14 @@ export function normalizeNativeShiftEnterInput(data, shouldDetectNativeShiftEnte
75
76
  return NATIVE_SHIFT_ENTER_SEQUENCE;
76
77
  return data;
77
78
  }
79
+ /**
80
+ * Terminal.app can deliver Shift+Return as ESC CR, which legacy key parsing reads as Alt+Enter.
81
+ * The pressed native Shift keeps it apart from an Option-as-Meta Return, which stays Alt+Enter.
82
+ */
78
83
  export function normalizeAppleTerminalInput(data, isAppleTerminal, isShiftPressed) {
84
+ if (isAppleTerminal && data === APPLE_TERMINAL_ESCAPED_RETURN_SEQUENCE && isShiftPressed) {
85
+ return NATIVE_SHIFT_ENTER_SEQUENCE;
86
+ }
79
87
  return normalizeNativeShiftEnterInput(data, isAppleTerminal, isShiftPressed);
80
88
  }
81
89
  export function isWarpWslSession(env = process.env, platform = process.platform, socketExists = (socketPath) => {
@@ -571,8 +579,13 @@ export class ProcessTerminal {
571
579
  forwardInputSequence(sequence) {
572
580
  if (!this.inputHandler)
573
581
  return;
574
- const shouldDetectNativeShiftEnter = sequence === "\r" && (isAppleTerminalSession() || process.platform === "win32");
575
- const input = normalizeNativeShiftEnterInput(sequence, shouldDetectNativeShiftEnter, shouldDetectNativeShiftEnter && isNativeModifierPressed("shift"));
582
+ const isReturn = sequence === "\r" || sequence === APPLE_TERMINAL_ESCAPED_RETURN_SEQUENCE;
583
+ const isAppleTerminal = isReturn && isAppleTerminalSession();
584
+ const shouldDetectNativeShiftEnter = isAppleTerminal || (sequence === "\r" && process.platform === "win32");
585
+ const isShiftPressed = shouldDetectNativeShiftEnter && isNativeModifierPressed("shift");
586
+ const input = isAppleTerminal
587
+ ? normalizeAppleTerminalInput(sequence, true, isShiftPressed)
588
+ : normalizeNativeShiftEnterInput(sequence, shouldDetectNativeShiftEnter, isShiftPressed);
576
589
  this.inputHandler(input);
577
590
  }
578
591
  enableModifyOtherKeys() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-tui",
3
- "version": "2026.11.3-1",
3
+ "version": "2026.11.3-3",
4
4
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",