@sigx/terminal-zero 0.5.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Andreas Ekdahl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The shared design-system contract for SignalX terminal UIs.
3
+ *
4
+ * `@sigx/terminal-zero` is the design-system-neutral foundation that skin
5
+ * packages (`@sigx/terminal-ui`, …) build on. This module is the *vocabulary*
6
+ * they agree on — the structural/semantic color tokens every theme authors, and
7
+ * how each token degrades to a 16-color terminal — so switching skins or themes
8
+ * is a token remap, not a component rewrite.
9
+ *
10
+ * Rules of the contract:
11
+ * - Skins author themes against the `Theme` shape; they never redeclare the
12
+ * token names. Drift fails `pnpm typecheck`.
13
+ * - Hex values are the single source of truth. The renderer
14
+ * (`@sigx/runtime-terminal`) turns a hex into a truecolor / 256 / 16-color SGR
15
+ * escape; this contract decides which *token* maps to which 16-color ANSI name
16
+ * when a hex can't be shown (`FALLBACK_ALIAS`).
17
+ *
18
+ * Token groups (from the SigX-tui design spec):
19
+ * - structure: `bg panel chrome line fg dim faint shadow`
20
+ * - accent: `accent accentSoft accentText selSoft`
21
+ * - status: `success warn danger info`
22
+ * - ANSI core: `black red green yellow blue magenta cyan white` (themed, with a
23
+ * guaranteed 16-color fallback since they ARE ANSI names)
24
+ */
25
+ import type { Define } from '@sigx/runtime-core';
26
+ export type Mode = 'dark' | 'light';
27
+ /** Every token a component may reference. Keep this shape identical across themes. */
28
+ export interface Theme {
29
+ name: string;
30
+ mode: Mode;
31
+ bg: string;
32
+ panel: string;
33
+ chrome: string;
34
+ line: string;
35
+ fg: string;
36
+ dim: string;
37
+ faint: string;
38
+ shadow: string;
39
+ accent: string;
40
+ accentSoft: string;
41
+ accentText: string;
42
+ selSoft: string;
43
+ success: string;
44
+ warn: string;
45
+ danger: string;
46
+ info: string;
47
+ black: string;
48
+ red: string;
49
+ green: string;
50
+ yellow: string;
51
+ blue: string;
52
+ magenta: string;
53
+ cyan: string;
54
+ white: string;
55
+ }
56
+ /** A token name (anything in `Theme` except its metadata). */
57
+ export type ColorToken = keyof Omit<Theme, 'name' | 'mode'>;
58
+ /** A color value: a semantic token (autocompleted) OR a raw `#rrggbb` hex. */
59
+ export type ColorValue = ColorToken | (string & {});
60
+ /** The 8 ANSI core token names — they degrade to themselves in 16-color mode. */
61
+ export declare const ANSI_NAMES: ReadonlySet<string>;
62
+ /**
63
+ * Which 16-color ANSI name each non-ANSI token degrades to when the terminal
64
+ * can't render truecolor/256. (Design knowledge — lives here, not in the
65
+ * renderer; the renderer only knows hex→SGR device math.)
66
+ */
67
+ export declare const FALLBACK_ALIAS: Record<string, string>;
68
+ /** Semantic/structural color of the component (`accent`, `danger`, `fg`, …). */
69
+ export type WithColor = Define.Prop<'color', ColorValue, false>;
70
+ /** Disabled: non-interactive + skin disabled styling. */
71
+ export type WithDisabled = Define.Prop<'disabled', boolean, false>;
72
+ /** Fieldset-style legend rendered into the top border. */
73
+ export type WithLabel = Define.Prop<'label', string, false>;
74
+ /** Auto-focus this control on mount. */
75
+ export type WithAutofocus = Define.Prop<'autofocus', boolean, false>;
@@ -0,0 +1,32 @@
1
+ //#region src/contract.ts
2
+ var e = new Set([
3
+ "black",
4
+ "red",
5
+ "green",
6
+ "yellow",
7
+ "blue",
8
+ "magenta",
9
+ "cyan",
10
+ "white"
11
+ ]), t = {
12
+ accent: "cyan",
13
+ accentSoft: "black",
14
+ accentText: "black",
15
+ selSoft: "black",
16
+ success: "green",
17
+ warn: "yellow",
18
+ danger: "red",
19
+ info: "cyan",
20
+ fg: "white",
21
+ dim: "white",
22
+ faint: "white",
23
+ line: "white",
24
+ shadow: "black",
25
+ bg: "black",
26
+ panel: "black",
27
+ chrome: "black"
28
+ };
29
+ //#endregion
30
+ export { e as ANSI_NAMES, t as FALLBACK_ALIAS };
31
+
32
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","names":[],"sources":["../src/contract.ts"],"sourcesContent":["/**\r\n * The shared design-system contract for SignalX terminal UIs.\r\n *\r\n * `@sigx/terminal-zero` is the design-system-neutral foundation that skin\r\n * packages (`@sigx/terminal-ui`, …) build on. This module is the *vocabulary*\r\n * they agree on — the structural/semantic color tokens every theme authors, and\r\n * how each token degrades to a 16-color terminal — so switching skins or themes\r\n * is a token remap, not a component rewrite.\r\n *\r\n * Rules of the contract:\r\n * - Skins author themes against the `Theme` shape; they never redeclare the\r\n * token names. Drift fails `pnpm typecheck`.\r\n * - Hex values are the single source of truth. The renderer\r\n * (`@sigx/runtime-terminal`) turns a hex into a truecolor / 256 / 16-color SGR\r\n * escape; this contract decides which *token* maps to which 16-color ANSI name\r\n * when a hex can't be shown (`FALLBACK_ALIAS`).\r\n *\r\n * Token groups (from the SigX-tui design spec):\r\n * - structure: `bg panel chrome line fg dim faint shadow`\r\n * - accent: `accent accentSoft accentText selSoft`\r\n * - status: `success warn danger info`\r\n * - ANSI core: `black red green yellow blue magenta cyan white` (themed, with a\r\n * guaranteed 16-color fallback since they ARE ANSI names)\r\n */\r\nimport type { Define } from '@sigx/runtime-core';\r\n\r\nexport type Mode = 'dark' | 'light';\r\n\r\n/** Every token a component may reference. Keep this shape identical across themes. */\r\nexport interface Theme {\r\n name: string;\r\n mode: Mode;\r\n\r\n // structure\r\n bg: string; // canvas / terminal background\r\n panel: string; // raised surface\r\n chrome: string; // title bars / footers\r\n line: string; // idle borders, rules\r\n fg: string; // default text\r\n dim: string; // muted text\r\n faint: string; // faintest text / progress track\r\n shadow: string; // drop-shadow tone (▒)\r\n\r\n // accent (focus + selection)\r\n accent: string;\r\n accentSoft: string; // subtle focus tint behind content\r\n accentText: string; // text on a solid accent fill\r\n selSoft: string; // selected-row tint (Select / Radio)\r\n\r\n // status\r\n success: string;\r\n warn: string;\r\n danger: string;\r\n info: string;\r\n\r\n // ANSI core (themed); each also has a guaranteed 16-color SGR fallback\r\n black: string;\r\n red: string;\r\n green: string;\r\n yellow: string;\r\n blue: string;\r\n magenta: string;\r\n cyan: string;\r\n white: string;\r\n}\r\n\r\n/** A token name (anything in `Theme` except its metadata). */\r\nexport type ColorToken = keyof Omit<Theme, 'name' | 'mode'>;\r\n\r\n/** A color value: a semantic token (autocompleted) OR a raw `#rrggbb` hex. */\r\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/ban-types\r\nexport type ColorValue = ColorToken | (string & {});\r\n\r\n/** The 8 ANSI core token names — they degrade to themselves in 16-color mode. */\r\nexport const ANSI_NAMES: ReadonlySet<string> = new Set([\r\n 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',\r\n]);\r\n\r\n/**\r\n * Which 16-color ANSI name each non-ANSI token degrades to when the terminal\r\n * can't render truecolor/256. (Design knowledge — lives here, not in the\r\n * renderer; the renderer only knows hex→SGR device math.)\r\n */\r\nexport const FALLBACK_ALIAS: Record<string, string> = {\r\n accent: 'cyan', accentSoft: 'black', accentText: 'black', selSoft: 'black',\r\n success: 'green', warn: 'yellow', danger: 'red', info: 'cyan',\r\n fg: 'white', dim: 'white', faint: 'white', line: 'white',\r\n shadow: 'black', bg: 'black', panel: 'black', chrome: 'black',\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// Common prop fragments — skin component props intersect these instead of\r\n// redeclaring the conventions. (`variant` is intentionally NOT here — fill style\r\n// is skin chrome and differs per design system.)\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Semantic/structural color of the component (`accent`, `danger`, `fg`, …). */\r\nexport type WithColor = Define.Prop<'color', ColorValue, false>;\r\n\r\n/** Disabled: non-interactive + skin disabled styling. */\r\nexport type WithDisabled = Define.Prop<'disabled', boolean, false>;\r\n\r\n/** Fieldset-style legend rendered into the top border. */\r\nexport type WithLabel = Define.Prop<'label', string, false>;\r\n\r\n/** Auto-focus this control on mount. */\r\nexport type WithAutofocus = Define.Prop<'autofocus', boolean, false>;\r\n"],"mappings":";AA0EA,IAAa,IAAkC,IAAI,IAAI;CACnD;CAAS;CAAO;CAAS;CAAU;CAAQ;CAAW;CAAQ;CACjE,CAAC,EAOW,IAAyC;CAClD,QAAQ;CAAQ,YAAY;CAAS,YAAY;CAAS,SAAS;CACnE,SAAS;CAAS,MAAM;CAAU,QAAQ;CAAO,MAAM;CACvD,IAAI;CAAS,KAAK;CAAS,OAAO;CAAS,MAAM;CACjD,QAAQ;CAAS,IAAI;CAAS,OAAO;CAAS,QAAQ;CACzD"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @sigx/terminal-zero — headless, design-system-neutral foundation for SignalX
3
+ * terminal UIs. Provides the token contract, the theme engine, shared glyphs +
4
+ * focus/input re-exports, and layout primitives. Design-system skins
5
+ * (`@sigx/terminal-ui`) build styled components on top.
6
+ */
7
+ export * from './contract';
8
+ export * from './theme';
9
+ export * from './shared';
10
+ export * from './layout';
11
+ export * from './prompts';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { ANSI_NAMES as e, FALLBACK_ALIAS as t } from "./contract.js";
2
+ import { applyThemeCanvas as n, disableThemeCanvas as r, getActiveTheme as i, getTheme as a, hasTheme as o, listThemes as s, registerTheme as c, resolveColor as l, setTheme as u } from "./theme/index.js";
3
+ import { A as d, B as f, C as p, D as m, E as h, F as g, G as _, H as v, I as y, J as b, K as x, L as S, M as C, N as w, O as T, P as E, R as D, S as O, T as k, U as A, V as j, W as M, _ as N, a as P, b as F, c as I, d as L, f as R, g as z, h as B, i as V, j as H, k as U, l as W, m as G, n as K, o as q, p as J, q as Y, r as X, s as Z, t as Q, u as $, v as ee, w as te, x as ne, y as re, z as ie } from "./shared-h5p-EcZV.js";
4
+ import { a as ae, i as oe, n as se, o as ce, r as le, s as ue, t as de } from "./layout-BRHtLdVH.js";
5
+ import { CANCEL as fe, __setInteractiveOverride as pe, enqueue as me, isBackspace as he, isCancel as ge, isCtrlC as _e, isDown as ve, isEnter as ye, isEsc as be, isInteractive as xe, isLeft as Se, isPrintable as Ce, isRight as we, isSpace as Te, isUp as Ee, paintToken as De, runPrompt as Oe, summaryLine as ke, tokenSgr as Ae } from "./prompts/index.js";
6
+ export { e as ANSI_NAMES, ue as Box, fe as CANCEL, ce as Col, oe as Divider, t as FALLBACK_ALIAS, Q as GLYPHS, de as Heading, K as PRESS_MS, X as READY_DELAY_MS, le as Row, V as SPINNERS, ae as Spacer, j as TICK_MS, se as Text, pe as __setInteractiveOverride, n as applyThemeCanvas, P as charWidth, U as createViewStack, d as cursorToRowCol, H as deleteAt, C as deleteBefore, r as disableThemeCanvas, q as dispatchKey, Z as displayWidth, me as enqueue, I as focus, W as focusNext, $ as focusPrev, L as focusState, T as generateQR, i as getActiveTheme, R as getColorDepth, J as getOutputTarget, G as getTerminalSize, a as getTheme, v as getTick, M as gradient, o as hasTheme, B as hexToSGR, _ as hueShift, w as insertAt, he as isBackspace, ge as isCancel, _e as isCtrlC, ve as isDown, ye as isEnter, be as isEsc, xe as isInteractive, Se as isLeft, Ce as isPrintable, we as isRight, Te as isSpace, Ee as isUp, E as layoutText, s as listThemes, x as mixHex, g as moveLeft, y as moveLineEnd, S as moveLineStart, D as moveRight, ie as moveVertical, z as onKey, De as paintToken, Y as parseHex, N as printStatic, ee as registerFocusable, c as registerTheme, re as renderTerminal, F as resolveBg, l as resolveColor, ne as resolveFg, b as rgbToHex, f as rowColToCursor, Oe as runPrompt, O as setScreenBackground, p as setScreenForeground, u as setTheme, A as subscribeTicker, ke as summaryLine, te as syncTerminalSize, Ae as tokenSgr, k as truncateToWidth, h as unregisterFocusable, m as writeStatic };
@@ -0,0 +1,25 @@
1
+ /** @jsxImportSource @sigx/runtime-core */
2
+ import { type Define } from '@sigx/runtime-core';
3
+ /**
4
+ * Themed container. A thin wrapper over the renderer's `<box>` that resolves
5
+ * token-named colors and defaults to a rounded `line` border with a themed
6
+ * drop shadow. Skins compose this; raw `<box>` stays available for the renderer.
7
+ */
8
+ export declare const Box: import("@sigx/runtime-core").ComponentFactory<{
9
+ border?: "double" | "none" | "rounded" | "single" | "thick" | undefined;
10
+ } & {
11
+ borderColor?: string | undefined;
12
+ } & {
13
+ backgroundColor?: string | undefined;
14
+ } & {
15
+ label?: string | undefined;
16
+ } & {
17
+ labelColor?: string | undefined;
18
+ } & {
19
+ padX?: number | undefined;
20
+ } & {
21
+ dropShadow?: boolean | undefined;
22
+ } & Define.Slot<"default", void>, void, {
23
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
24
+ }>;
25
+ export default Box;
@@ -0,0 +1,15 @@
1
+ /** @jsxImportSource @sigx/runtime-core */
2
+ import { type Define } from '@sigx/runtime-core';
3
+ /**
4
+ * Vertical stack. The renderer already lays block children out top-to-bottom,
5
+ * so `Col` is a borderless grouping box with optional spacing: `gap` inserts
6
+ * that many blank rows BETWEEN children (not before or after) — the
7
+ * component-layer replacement for `<box></box>` spacer hacks. For
8
+ * side-by-side columns, see `Row`.
9
+ */
10
+ export declare const Col: import("@sigx/runtime-core").ComponentFactory<{
11
+ gap?: number | undefined;
12
+ } & Define.Slot<"default", void>, void, {
13
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
14
+ }>;
15
+ export default Col;
@@ -0,0 +1,9 @@
1
+ /** Horizontal rule of `width` cells, with an optional centered accent label. */
2
+ export declare const Divider: import("@sigx/runtime-core").ComponentFactory<{
3
+ width?: number | undefined;
4
+ } & {
5
+ label?: string | undefined;
6
+ } & {
7
+ color?: string | undefined;
8
+ }, void, {}>;
9
+ export default Divider;
@@ -0,0 +1,18 @@
1
+ /** @jsxImportSource @sigx/runtime-core */
2
+ import { type Define } from '@sigx/runtime-core';
3
+ /**
4
+ * Horizontal layout: each slot child renders as a column, merged side by side
5
+ * (the renderer pads every column to its own display width and zips the lines).
6
+ * `gap` is the spacing between columns (default 2); `align` places shorter
7
+ * columns at the start (default), center, or end — 'top'/'bottom' are
8
+ * accepted aliases. A row wider than the terminal clips at the right edge —
9
+ * put the column that must stay intact leftmost.
10
+ */
11
+ export declare const Row: import("@sigx/runtime-core").ComponentFactory<Define.Slot<"default", void> & {
12
+ gap?: number | undefined;
13
+ } & {
14
+ align?: "bottom" | "center" | "end" | "start" | "top" | undefined;
15
+ }, void, {
16
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
17
+ }>;
18
+ export default Row;
@@ -0,0 +1,5 @@
1
+ /** Vertical gap of `size` blank lines (default 1). */
2
+ export declare const Spacer: import("@sigx/runtime-core").ComponentFactory<{
3
+ size?: number | undefined;
4
+ }, void, {}>;
5
+ export default Spacer;
@@ -0,0 +1,50 @@
1
+ /** @jsxImportSource @sigx/runtime-core */
2
+ import { type Define } from '@sigx/runtime-core';
3
+ /**
4
+ * Typography span — the token-aware way to write styled text without
5
+ * touching `resolveColor` or raw `<text>`:
6
+ *
7
+ * <Text color="dim">Scan with sigx-lynx-go:</Text>
8
+ * <Text color="danger" bold>failed</Text>
9
+ *
10
+ * `color`/`bg` accept theme tokens or `#hex`. Style flags map to terminal
11
+ * SGR attributes (rendering depends on the emulator; all are gated off for
12
+ * piped/CI output).
13
+ *
14
+ * NOTE — deliberately INLINE, unlike every other component: Text is a span
15
+ * that composes inside a line (`<Text color="accent">{n}</Text> items`).
16
+ * Standalone lines use `Heading` or wrap in a layout component.
17
+ *
18
+ * There is no `size`/`weight` enum: terminal cells have exactly one size;
19
+ * weight is the `bold`/`faint` pair. This mirrors the daisy/lynx `Text`
20
+ * shape (discrete props, no variant) minus what a terminal cannot do.
21
+ */
22
+ export declare const Text: import("@sigx/runtime-core").ComponentFactory<{
23
+ color?: string | undefined;
24
+ } & {
25
+ bg?: string | undefined;
26
+ } & {
27
+ bold?: boolean | undefined;
28
+ } & {
29
+ faint?: boolean | undefined;
30
+ } & {
31
+ italic?: boolean | undefined;
32
+ } & {
33
+ underline?: boolean | undefined;
34
+ } & {
35
+ lineThrough?: boolean | undefined;
36
+ } & {
37
+ inverse?: boolean | undefined;
38
+ } & Define.Slot<"default", void>, void, {
39
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
40
+ }>;
41
+ /**
42
+ * A standalone bold line — the terminal analog of a heading. Block element
43
+ * (its own line), default color `fg`.
44
+ */
45
+ export declare const Heading: import("@sigx/runtime-core").ComponentFactory<{
46
+ color?: string | undefined;
47
+ } & Define.Slot<"default", void>, void, {
48
+ default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
49
+ }>;
50
+ export default Text;
@@ -0,0 +1,6 @@
1
+ export { Box } from './Box';
2
+ export { Col } from './Col';
3
+ export { Spacer } from './Spacer';
4
+ export { Divider } from './Divider';
5
+ export { Row } from './Row';
6
+ export { Text, Heading } from './Text';
@@ -0,0 +1,2 @@
1
+ import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "../layout-BRHtLdVH.js";
2
+ export { a as Box, r as Col, t as Divider, o as Heading, i as Row, e as Spacer, n as Text };
@@ -0,0 +1,71 @@
1
+ import { resolveColor as e } from "./theme/index.js";
2
+ import { displayWidth as t } from "@sigx/runtime-terminal";
3
+ import { component as n } from "@sigx/runtime-core";
4
+ import { jsx as r, jsxs as i } from "@sigx/runtime-core/jsx-runtime";
5
+ //#region src/layout/Box.tsx
6
+ var a = n(({ props: t, slots: n }) => () => /* @__PURE__ */ r("box", {
7
+ border: t.border ?? "rounded",
8
+ borderColor: e(t.borderColor ?? "line"),
9
+ backgroundColor: t.backgroundColor ? e(t.backgroundColor) : void 0,
10
+ label: t.label,
11
+ labelColor: e(t.labelColor ?? "accent"),
12
+ padX: t.padX,
13
+ dropShadow: t.dropShadow,
14
+ shadowColor: e("shadow"),
15
+ children: n.default?.()
16
+ }), { name: "Box" }), o = n(({ props: e, slots: t }) => () => {
17
+ let n = t.default?.(), i = Math.max(0, e.gap ?? 0);
18
+ if (!i || !Array.isArray(n) || n.length < 2) return /* @__PURE__ */ r("box", { children: n });
19
+ let a = [];
20
+ return n.forEach((e, t) => {
21
+ if (t > 0) for (let e = 0; e < i; e++) a.push(/* @__PURE__ */ r("box", { children: /* @__PURE__ */ r("text", { children: " " }) }));
22
+ a.push(e);
23
+ }), /* @__PURE__ */ r("box", { children: a });
24
+ }, { name: "Col" }), s = n(({ props: e }) => () => {
25
+ let t = Math.max(1, e.size ?? 1), n = [];
26
+ for (let e = 0; e < t - 1; e++) n.push(/* @__PURE__ */ r("br", {}));
27
+ return /* @__PURE__ */ r("box", { children: n });
28
+ }, { name: "Spacer" }), c = n(({ props: n }) => () => {
29
+ let a = Math.max(1, n.width ?? 24), o = e(n.color ?? "line"), s = n.label;
30
+ if (!s) return /* @__PURE__ */ r("box", { children: /* @__PURE__ */ r("text", {
31
+ color: o,
32
+ children: "─".repeat(a)
33
+ }) });
34
+ let c = ` ${s} `, l = Math.max(0, a - t(c)), u = Math.floor(l / 2), d = l - u;
35
+ return /* @__PURE__ */ i("box", { children: [
36
+ /* @__PURE__ */ r("text", {
37
+ color: o,
38
+ children: "─".repeat(u)
39
+ }),
40
+ /* @__PURE__ */ r("text", {
41
+ color: e("accent"),
42
+ children: c
43
+ }),
44
+ /* @__PURE__ */ r("text", {
45
+ color: o,
46
+ children: "─".repeat(d)
47
+ })
48
+ ] });
49
+ }, { name: "Divider" }), l = n(({ props: e, slots: t }) => () => /* @__PURE__ */ r("row", {
50
+ gap: e.gap ?? 2,
51
+ align: e.align ?? "start",
52
+ children: t.default?.()
53
+ }), { name: "Row" }), u = n(({ props: t, slots: n }) => () => /* @__PURE__ */ r("text", {
54
+ color: t.color ? e(t.color) : void 0,
55
+ backgroundColor: t.bg ? e(t.bg) : void 0,
56
+ bold: t.bold,
57
+ faint: t.faint,
58
+ italic: t.italic,
59
+ underline: t.underline,
60
+ lineThrough: t.lineThrough,
61
+ inverse: t.inverse,
62
+ children: n.default?.()
63
+ }), { name: "Text" }), d = n(({ props: t, slots: n }) => () => /* @__PURE__ */ r("box", { children: /* @__PURE__ */ r("text", {
64
+ bold: !0,
65
+ color: e(t.color ?? "fg"),
66
+ children: n.default?.()
67
+ }) }), { name: "Heading" });
68
+ //#endregion
69
+ export { s as a, c as i, u as n, o, l as r, a as s, d as t };
70
+
71
+ //# sourceMappingURL=layout-BRHtLdVH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-BRHtLdVH.js","names":[],"sources":["../src/layout/Box.tsx","../src/layout/Col.tsx","../src/layout/Spacer.tsx","../src/layout/Divider.tsx","../src/layout/Row.tsx","../src/layout/Text.tsx"],"sourcesContent":["/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor } from '../theme';\r\n\r\n/**\r\n * Themed container. A thin wrapper over the renderer's `<box>` that resolves\r\n * token-named colors and defaults to a rounded `line` border with a themed\r\n * drop shadow. Skins compose this; raw `<box>` stays available for the renderer.\r\n */\r\nexport const Box = component<\r\n Define.Prop<'border', 'single' | 'double' | 'rounded' | 'thick' | 'none', false> &\r\n Define.Prop<'borderColor', string, false> &\r\n Define.Prop<'backgroundColor', string, false> &\r\n Define.Prop<'label', string, false> &\r\n Define.Prop<'labelColor', string, false> &\r\n Define.Prop<'padX', number, false> &\r\n Define.Prop<'dropShadow', boolean, false> &\r\n Define.Slot<'default'>\r\n>(({ props, slots }) => {\r\n return () => (\r\n <box\r\n border={props.border ?? 'rounded'}\r\n borderColor={resolveColor(props.borderColor ?? 'line')}\r\n backgroundColor={props.backgroundColor ? resolveColor(props.backgroundColor) : undefined}\r\n label={props.label}\r\n labelColor={resolveColor(props.labelColor ?? 'accent')}\r\n padX={props.padX}\r\n dropShadow={props.dropShadow}\r\n shadowColor={resolveColor('shadow')}\r\n >\r\n {slots.default?.()}\r\n </box>\r\n );\r\n}, { name: 'Box' });\r\n\r\nexport default Box;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\n\r\n/**\r\n * Vertical stack. The renderer already lays block children out top-to-bottom,\r\n * so `Col` is a borderless grouping box with optional spacing: `gap` inserts\r\n * that many blank rows BETWEEN children (not before or after) — the\r\n * component-layer replacement for `<box></box>` spacer hacks. For\r\n * side-by-side columns, see `Row`.\r\n */\r\nexport const Col = component<\r\n Define.Prop<'gap', number, false> &\r\n Define.Slot<'default'>\r\n>(({ props, slots }) => {\r\n return () => {\r\n const children = slots.default?.();\r\n const gap = Math.max(0, props.gap ?? 0);\r\n if (!gap || !Array.isArray(children) || children.length < 2) {\r\n return <box>{children}</box>;\r\n }\r\n const spaced: unknown[] = [];\r\n children.forEach((child, i) => {\r\n if (i > 0) {\r\n for (let g = 0; g < gap; g++) {\r\n spaced.push(<box><text> </text></box>);\r\n }\r\n }\r\n spaced.push(child);\r\n });\r\n return <box>{spaced}</box>;\r\n };\r\n}, { name: 'Col' });\r\n\r\nexport default Col;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\n\r\n/** Vertical gap of `size` blank lines (default 1). */\r\nexport const Spacer = component<\r\n Define.Prop<'size', number, false>\r\n>(({ props }) => {\r\n return () => {\r\n const size = Math.max(1, props.size ?? 1);\r\n // A <box> already renders one line; each <br/> adds another — so\r\n // exactly `size` blank lines means size-1 breaks.\r\n const breaks = [];\r\n for (let i = 0; i < size - 1; i++) breaks.push(<br />);\r\n return <box>{breaks}</box>;\r\n };\r\n}, { name: 'Spacer' });\r\n\r\nexport default Spacer;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { displayWidth } from '@sigx/runtime-terminal';\r\nimport { resolveColor } from '../theme';\r\n\r\n/** Horizontal rule of `width` cells, with an optional centered accent label. */\r\nexport const Divider = component<\r\n Define.Prop<'width', number, false> &\r\n Define.Prop<'label', string, false> &\r\n Define.Prop<'color', string, false>\r\n>(({ props }) => {\r\n return () => {\r\n const width = Math.max(1, props.width ?? 24);\r\n const ruleColor = resolveColor(props.color ?? 'line');\r\n const label = props.label;\r\n\r\n if (!label) {\r\n return <box><text color={ruleColor}>{'─'.repeat(width)}</text></box>;\r\n }\r\n\r\n const inner = ` ${label} `;\r\n // Measure in terminal cells, not UTF-16 units — wide glyphs (CJK,\r\n // emoji) and combining marks would otherwise mis-center the label.\r\n const remain = Math.max(0, width - displayWidth(inner));\r\n const left = Math.floor(remain / 2);\r\n const right = remain - left;\r\n return (\r\n <box>\r\n <text color={ruleColor}>{'─'.repeat(left)}</text>\r\n <text color={resolveColor('accent')}>{inner}</text>\r\n <text color={ruleColor}>{'─'.repeat(right)}</text>\r\n </box>\r\n );\r\n };\r\n}, { name: 'Divider' });\r\n\r\nexport default Divider;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\n\r\n/**\r\n * Horizontal layout: each slot child renders as a column, merged side by side\r\n * (the renderer pads every column to its own display width and zips the lines).\r\n * `gap` is the spacing between columns (default 2); `align` places shorter\r\n * columns at the start (default), center, or end — 'top'/'bottom' are\r\n * accepted aliases. A row wider than the terminal clips at the right edge —\r\n * put the column that must stay intact leftmost.\r\n */\r\nexport const Row = component<\r\n Define.Slot<'default'> &\r\n Define.Prop<'gap', number, false> &\r\n Define.Prop<'align', 'start' | 'center' | 'end' | 'top' | 'bottom', false>\r\n>(({ props, slots }) => {\r\n return () => (\r\n <row gap={props.gap ?? 2} align={props.align ?? 'start'}>\r\n {slots.default?.()}\r\n </row>\r\n );\r\n}, { name: 'Row' });\r\n\r\nexport default Row;\r\n","/** @jsxImportSource @sigx/runtime-core */\r\nimport { component, type Define } from '@sigx/runtime-core';\r\nimport { resolveColor } from '../theme';\r\n\r\n/**\r\n * Typography span — the token-aware way to write styled text without\r\n * touching `resolveColor` or raw `<text>`:\r\n *\r\n * <Text color=\"dim\">Scan with sigx-lynx-go:</Text>\r\n * <Text color=\"danger\" bold>failed</Text>\r\n *\r\n * `color`/`bg` accept theme tokens or `#hex`. Style flags map to terminal\r\n * SGR attributes (rendering depends on the emulator; all are gated off for\r\n * piped/CI output).\r\n *\r\n * NOTE — deliberately INLINE, unlike every other component: Text is a span\r\n * that composes inside a line (`<Text color=\"accent\">{n}</Text> items`).\r\n * Standalone lines use `Heading` or wrap in a layout component.\r\n *\r\n * There is no `size`/`weight` enum: terminal cells have exactly one size;\r\n * weight is the `bold`/`faint` pair. This mirrors the daisy/lynx `Text`\r\n * shape (discrete props, no variant) minus what a terminal cannot do.\r\n */\r\nexport const Text = component<\r\n Define.Prop<'color', string, false> &\r\n Define.Prop<'bg', string, false> &\r\n Define.Prop<'bold', boolean, false> &\r\n Define.Prop<'faint', boolean, false> &\r\n Define.Prop<'italic', boolean, false> &\r\n Define.Prop<'underline', boolean, false> &\r\n Define.Prop<'lineThrough', boolean, false> &\r\n Define.Prop<'inverse', boolean, false> &\r\n Define.Slot<'default'>\r\n>(({ props, slots }) => {\r\n return () => (\r\n <text\r\n color={props.color ? resolveColor(props.color) : undefined}\r\n backgroundColor={props.bg ? resolveColor(props.bg) : undefined}\r\n bold={props.bold}\r\n faint={props.faint}\r\n italic={props.italic}\r\n underline={props.underline}\r\n lineThrough={props.lineThrough}\r\n inverse={props.inverse}\r\n >\r\n {slots.default?.()}\r\n </text>\r\n );\r\n}, { name: 'Text' });\r\n\r\n/**\r\n * A standalone bold line — the terminal analog of a heading. Block element\r\n * (its own line), default color `fg`.\r\n */\r\nexport const Heading = component<\r\n Define.Prop<'color', string, false> &\r\n Define.Slot<'default'>\r\n>(({ props, slots }) => {\r\n return () => (\r\n <box>\r\n <text bold color={resolveColor(props.color ?? 'fg')}>\r\n {slots.default?.()}\r\n </text>\r\n </box>\r\n );\r\n}, { name: 'Heading' });\r\n\r\nexport default Text;\r\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Cancellation marker for the imperative prompts. `Symbol.for` so duplicated
3
+ * module instances (mixed dist/src resolution in a workspace) still agree.
4
+ */
5
+ export declare const CANCEL: unique symbol;
6
+ /** True when a prompt result means the user cancelled (Esc / Ctrl+C). */
7
+ export declare function isCancel(value: unknown): value is typeof CANCEL;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Headless prompt engine: the cancellation symbol, key predicates, and the
3
+ * one-shot mount/serialization/summary machinery that imperative prompts run
4
+ * on. UI-agnostic — the styled prompt functions (text/select/multiselect/
5
+ * confirm and their styled chrome) live in `@sigx/terminal-ui/prompts`;
6
+ * any skin built on this foundation reuses the engine and brings its own
7
+ * views.
8
+ */
9
+ export * from './cancel';
10
+ export * from './keys';
11
+ export * from './runPrompt';
@@ -0,0 +1,53 @@
1
+ import { resolveColor as e } from "../theme/index.js";
2
+ import { getOutputTarget as t, printStatic as n, renderTerminal as r, resolveFg as i, writeStatic as a } from "@sigx/runtime-terminal";
3
+ //#region src/prompts/cancel.ts
4
+ var o = Symbol.for("sigx.prompt.cancel");
5
+ function s(e) {
6
+ return e === o;
7
+ }
8
+ //#endregion
9
+ //#region src/prompts/keys.ts
10
+ var c = "\x1B", l = "", u = "", d = "\b", f = (e) => e === "\r" || e === "\n", p = (e) => e === " ", m = (e) => e === c, h = (e) => e === l, g = (e) => e === u || e === d, _ = (e) => e === c + "[A", v = (e) => e === c + "[B", y = (e) => e === c + "[C", b = (e) => e === c + "[D", x = (e) => e.length === 1 && e >= " " && e !== u, S = Promise.resolve(), C;
11
+ function w(e) {
12
+ C = e;
13
+ }
14
+ function T() {
15
+ return C === void 0 ? t().isTTY && !!process.stdin.isTTY : C;
16
+ }
17
+ function E(t) {
18
+ return i(e(t));
19
+ }
20
+ function D(e, t) {
21
+ let n = E(t);
22
+ return n ? `${n}${e}\x1b[39m` : e;
23
+ }
24
+ function O(e, t, n) {
25
+ if (e === "cancel") return `${D("■", "danger")} ${t} ${D("· cancelled", "dim")}`;
26
+ let r = n !== void 0 && n !== "" ? ` ${D(`· ${n}`, "dim")}` : "";
27
+ return `${D("◇", "success")} ${t}${r}`;
28
+ }
29
+ function k(e) {
30
+ let t = S.then(e);
31
+ return S = t.then(() => void 0, () => void 0), t;
32
+ }
33
+ function A(e) {
34
+ return k(() => {
35
+ if (!T()) {
36
+ let t = e.fallback?.();
37
+ return t && t.ok ? (n(O("done", e.message, e.display(t.value))), Promise.resolve(t.value)) : Promise.reject(/* @__PURE__ */ Error(`Prompt "${e.message}" requires an interactive terminal — pass the value via a CLI flag or provide initialValue.`));
38
+ }
39
+ return new Promise((t) => {
40
+ let n = !1, i = r(e.build((r) => {
41
+ n || (n = !0, i.unmount(), a(r === o ? O("cancel", e.message) : O("done", e.message, e.display(r))), t(r));
42
+ }), {
43
+ mode: "inline",
44
+ persistOnExit: !1,
45
+ exitOnCtrlC: !1
46
+ });
47
+ });
48
+ });
49
+ }
50
+ //#endregion
51
+ export { o as CANCEL, w as __setInteractiveOverride, k as enqueue, g as isBackspace, s as isCancel, h as isCtrlC, v as isDown, f as isEnter, m as isEsc, T as isInteractive, b as isLeft, x as isPrintable, y as isRight, p as isSpace, _ as isUp, D as paintToken, A as runPrompt, O as summaryLine, E as tokenSgr };
52
+
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/prompts/cancel.ts","../../src/prompts/keys.ts","../../src/prompts/runPrompt.ts"],"sourcesContent":["/**\r\n * Cancellation marker for the imperative prompts. `Symbol.for` so duplicated\r\n * module instances (mixed dist/src resolution in a workspace) still agree.\r\n */\r\nexport const CANCEL: unique symbol = Symbol.for('sigx.prompt.cancel') as never;\r\n\r\n/** True when a prompt result means the user cancelled (Esc / Ctrl+C). */\r\nexport function isCancel(value: unknown): value is typeof CANCEL {\r\n return value === CANCEL;\r\n}\r\n","/**\r\n * Shared key predicates for prompt input. Constants are built with\r\n * fromCharCode so no raw control bytes live in source. Esc is detected by\r\n * exact match: Node delivers complete escape sequences per data event, so a\r\n * lone ESC byte is the Esc key, while arrows arrive as multi-byte chunks.\r\n */\r\nconst ESC = String.fromCharCode(27);\r\nconst CTRL_C = String.fromCharCode(3);\r\nconst DEL = String.fromCharCode(127);\r\nconst BS = String.fromCharCode(8);\r\n\r\nexport const isEnter = (key: string): boolean => key === '\\r' || key === '\\n';\r\nexport const isSpace = (key: string): boolean => key === ' ';\r\nexport const isEsc = (key: string): boolean => key === ESC;\r\nexport const isCtrlC = (key: string): boolean => key === CTRL_C;\r\n/** DEL on most terminals, BS on some Windows shells. */\r\nexport const isBackspace = (key: string): boolean => key === DEL || key === BS;\r\nexport const isUp = (key: string): boolean => key === ESC + '[A';\r\nexport const isDown = (key: string): boolean => key === ESC + '[B';\r\nexport const isRight = (key: string): boolean => key === ESC + '[C';\r\nexport const isLeft = (key: string): boolean => key === ESC + '[D';\r\n/** A single printable character (what a text field appends). */\r\nexport const isPrintable = (key: string): boolean => key.length === 1 && key >= ' ' && key !== DEL;\r\n","/**\r\n * One-shot mount plumbing for the imperative prompts: each prompt mounts an\r\n * inline app with `persistOnExit: false` (the UI vanishes on settle) and\r\n * `exitOnCtrlC: false` (Ctrl+C becomes a cancel). On settle the live region\r\n * is erased by the unmount and a permanent summary line takes its place —\r\n * a finished wizard reads as a tidy `◇`-transcript in scrollback.\r\n *\r\n * Concurrent calls SERIALIZE through a module-level promise chain — the\r\n * renderer is a singleton, so two live prompts can never race it.\r\n */\r\nimport {\r\n renderTerminal, getOutputTarget, writeStatic, printStatic, resolveFg,\r\n} from '@sigx/runtime-terminal';\r\nimport { resolveColor } from '../theme';\r\nimport { CANCEL } from './cancel';\r\n\r\nlet chain: Promise<unknown> = Promise.resolve();\r\nlet interactiveOverride: boolean | undefined;\r\n\r\n/** Test seam: force the interactive/non-interactive decision. */\r\nexport function __setInteractiveOverride(value?: boolean): void {\r\n interactiveOverride = value;\r\n}\r\n\r\n/** Both ends must be a TTY: output for the live region, stdin for the keys. */\r\nexport function isInteractive(): boolean {\r\n if (interactiveOverride !== undefined) return interactiveOverride;\r\n return getOutputTarget().isTTY && !!process.stdin.isTTY;\r\n}\r\n\r\n/** Foreground SGR for a theme token; '' at depth none (plain output). */\r\nexport function tokenSgr(token: string): string {\r\n return resolveFg(resolveColor(token));\r\n}\r\n\r\n/** Wrap text in a token color, with default-fg restore (never a full reset). */\r\nexport function paintToken(text: string, token: string): string {\r\n const code = tokenSgr(token);\r\n return code ? `${code}${text}\\x1b[39m` : text;\r\n}\r\n\r\nexport function summaryLine(kind: 'done' | 'cancel', message: string, display?: string): string {\r\n if (kind === 'cancel') {\r\n return `${paintToken('■', 'danger')} ${message} ${paintToken('· cancelled', 'dim')}`;\r\n }\r\n const tail = display !== undefined && display !== '' ? ` ${paintToken(`· ${display}`, 'dim')}` : '';\r\n return `${paintToken('◇', 'success')} ${message}${tail}`;\r\n}\r\n\r\n/**\r\n * Serialize a task behind any prompts already in flight. Exposed to the\r\n * prompt spinner so it occupies the live region exclusively too.\r\n */\r\nexport function enqueue<T>(task: () => Promise<T>): Promise<T> {\r\n const p = chain.then(task);\r\n chain = p.then(() => undefined, () => undefined);\r\n return p;\r\n}\r\n\r\nexport interface RunPromptSpec<R> {\r\n message: string;\r\n /** Build the prompt vnode; call `done` exactly once to settle. */\r\n build: (done: (result: R | typeof CANCEL) => void) => unknown;\r\n /** Human display of the result for the `◇ message · display` summary. */\r\n display: (result: R) => string;\r\n /** Non-interactive fallback (the prompt's initialValue, when provided). */\r\n fallback?: () => { ok: true; value: R } | { ok: false };\r\n}\r\n\r\n/**\r\n * Note on the event loop: a prompt resolves inside the stdin data handler, so\r\n * back-to-back awaited prompts never let the loop go empty. Pure-promise work\r\n * between prompts with no other pending handles could end the process —\r\n * real async work (fs, network, timers) keeps it alive on its own.\r\n */\r\nexport function runPrompt<R>(spec: RunPromptSpec<R>): Promise<R | typeof CANCEL> {\r\n const run = (): Promise<R | typeof CANCEL> => {\r\n if (!isInteractive()) {\r\n const fb = spec.fallback?.();\r\n if (fb && fb.ok) {\r\n printStatic(summaryLine('done', spec.message, spec.display(fb.value)));\r\n return Promise.resolve(fb.value);\r\n }\r\n return Promise.reject(new Error(\r\n `Prompt \"${spec.message}\" requires an interactive terminal — ` +\r\n 'pass the value via a CLI flag or provide initialValue.',\r\n ));\r\n }\r\n return new Promise<R | typeof CANCEL>((resolve) => {\r\n let settled = false;\r\n const done = (result: R | typeof CANCEL) => {\r\n if (settled) return;\r\n settled = true;\r\n // Order matters: unmount erases the live region and pauses\r\n // stdin; the unmounted writeStatic then prints the summary\r\n // exactly where the prompt stood.\r\n handle.unmount();\r\n writeStatic(result === CANCEL\r\n ? summaryLine('cancel', spec.message)\r\n : summaryLine('done', spec.message, spec.display(result as R)));\r\n resolve(result);\r\n };\r\n const handle = renderTerminal(spec.build(done), {\r\n mode: 'inline',\r\n persistOnExit: false,\r\n exitOnCtrlC: false,\r\n });\r\n });\r\n };\r\n return enqueue(run);\r\n}\r\n"],"mappings":";;;AAIA,IAAa,IAAwB,OAAO,IAAI,qBAAqB;AAGrE,SAAgB,EAAS,GAAwC;CAC7D,OAAO,MAAU;;;;ACFrB,IAAM,IAAM,QACN,IAAS,KACT,IAAM,KACN,IAAK,MAEE,KAAW,MAAyB,MAAQ,QAAQ,MAAQ,MAC5D,KAAW,MAAyB,MAAQ,KAC5C,KAAS,MAAyB,MAAQ,GAC1C,KAAW,MAAyB,MAAQ,GAE5C,KAAe,MAAyB,MAAQ,KAAO,MAAQ,GAC/D,KAAQ,MAAyB,MAAQ,IAAM,MAC/C,KAAU,MAAyB,MAAQ,IAAM,MACjD,KAAW,MAAyB,MAAQ,IAAM,MAClD,KAAU,MAAyB,MAAQ,IAAM,MAEjD,KAAe,MAAyB,EAAI,WAAW,KAAK,KAAO,OAAO,MAAQ,GCN3F,IAA0B,QAAQ,SAAS,EAC3C;AAGJ,SAAgB,EAAyB,GAAuB;CAC5D,IAAsB;;AAI1B,SAAgB,IAAyB;CAErC,OADI,MAAwB,KAAA,IACrB,GAAiB,CAAC,SAAS,CAAC,CAAC,QAAQ,MAAM,QADJ;;AAKlD,SAAgB,EAAS,GAAuB;CAC5C,OAAO,EAAU,EAAa,EAAM,CAAC;;AAIzC,SAAgB,EAAW,GAAc,GAAuB;CAC5D,IAAM,IAAO,EAAS,EAAM;CAC5B,OAAO,IAAO,GAAG,IAAO,EAAK,YAAY;;AAG7C,SAAgB,EAAY,GAAyB,GAAiB,GAA0B;CAC5F,IAAI,MAAS,UACT,OAAO,GAAG,EAAW,KAAK,SAAS,CAAC,GAAG,EAAQ,GAAG,EAAW,eAAe,MAAM;CAEtF,IAAM,IAAO,MAAY,KAAA,KAAa,MAAY,KAAK,IAAI,EAAW,KAAK,KAAW,MAAM,KAAK;CACjG,OAAO,GAAG,EAAW,KAAK,UAAU,CAAC,GAAG,IAAU;;AAOtD,SAAgB,EAAW,GAAoC;CAC3D,IAAM,IAAI,EAAM,KAAK,EAAK;CAE1B,OADA,IAAQ,EAAE,WAAW,KAAA,SAAiB,KAAA,EAAU,EACzC;;AAmBX,SAAgB,EAAa,GAAoD;CAkC7E,OAAO,QAjCuC;EAC1C,IAAI,CAAC,GAAe,EAAE;GAClB,IAAM,IAAK,EAAK,YAAY;GAK5B,OAJI,KAAM,EAAG,MACT,EAAY,EAAY,QAAQ,EAAK,SAAS,EAAK,QAAQ,EAAG,MAAM,CAAC,CAAC,EAC/D,QAAQ,QAAQ,EAAG,MAAM,IAE7B,QAAQ,OAAO,gBAAI,MACtB,WAAW,EAAK,QAAQ,6FAE3B,CAAC;;EAEN,OAAO,IAAI,SAA4B,MAAY;GAC/C,IAAI,IAAU,IAaR,IAAS,EAAe,EAAK,OAZrB,MAA8B;IACpC,MACJ,IAAU,IAIV,EAAO,SAAS,EAChB,EAAY,MAAW,IACjB,EAAY,UAAU,EAAK,QAAQ,GACnC,EAAY,QAAQ,EAAK,SAAS,EAAK,QAAQ,EAAY,CAAC,CAAC,EACnE,EAAQ,EAAO;KAE2B,EAAE;IAC5C,MAAM;IACN,eAAe;IACf,aAAa;IAChB,CAAC;IACJ;GAEa"}
@@ -0,0 +1,12 @@
1
+ export declare const isEnter: (key: string) => boolean;
2
+ export declare const isSpace: (key: string) => boolean;
3
+ export declare const isEsc: (key: string) => boolean;
4
+ export declare const isCtrlC: (key: string) => boolean;
5
+ /** DEL on most terminals, BS on some Windows shells. */
6
+ export declare const isBackspace: (key: string) => boolean;
7
+ export declare const isUp: (key: string) => boolean;
8
+ export declare const isDown: (key: string) => boolean;
9
+ export declare const isRight: (key: string) => boolean;
10
+ export declare const isLeft: (key: string) => boolean;
11
+ /** A single printable character (what a text field appends). */
12
+ export declare const isPrintable: (key: string) => boolean;
@@ -0,0 +1,36 @@
1
+ import { CANCEL } from './cancel';
2
+ /** Test seam: force the interactive/non-interactive decision. */
3
+ export declare function __setInteractiveOverride(value?: boolean): void;
4
+ /** Both ends must be a TTY: output for the live region, stdin for the keys. */
5
+ export declare function isInteractive(): boolean;
6
+ /** Foreground SGR for a theme token; '' at depth none (plain output). */
7
+ export declare function tokenSgr(token: string): string;
8
+ /** Wrap text in a token color, with default-fg restore (never a full reset). */
9
+ export declare function paintToken(text: string, token: string): string;
10
+ export declare function summaryLine(kind: 'done' | 'cancel', message: string, display?: string): string;
11
+ /**
12
+ * Serialize a task behind any prompts already in flight. Exposed to the
13
+ * prompt spinner so it occupies the live region exclusively too.
14
+ */
15
+ export declare function enqueue<T>(task: () => Promise<T>): Promise<T>;
16
+ export interface RunPromptSpec<R> {
17
+ message: string;
18
+ /** Build the prompt vnode; call `done` exactly once to settle. */
19
+ build: (done: (result: R | typeof CANCEL) => void) => unknown;
20
+ /** Human display of the result for the `◇ message · display` summary. */
21
+ display: (result: R) => string;
22
+ /** Non-interactive fallback (the prompt's initialValue, when provided). */
23
+ fallback?: () => {
24
+ ok: true;
25
+ value: R;
26
+ } | {
27
+ ok: false;
28
+ };
29
+ }
30
+ /**
31
+ * Note on the event loop: a prompt resolves inside the stdin data handler, so
32
+ * back-to-back awaited prompts never let the loop go empty. Pure-promise work
33
+ * between prompts with no other pending handles could end the process —
34
+ * real async work (fs, network, timers) keeps it alive on its own.
35
+ */
36
+ export declare function runPrompt<R>(spec: RunPromptSpec<R>): Promise<R | typeof CANCEL>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Pure color→color math for gradients and animated effects. Deliberately
3
+ * dependency-free: token→color stays in the theme layer, color→SGR in the
4
+ * renderer — this module only mixes concrete colors in between. Invalid input
5
+ * degrades to white instead of throwing; these run inside render functions.
6
+ */
7
+ export declare function parseHex(hex: string): [number, number, number] | null;
8
+ export declare function rgbToHex(r: number, g: number, b: number): string;
9
+ /** Per-channel linear mix of two hex colors; `t` clamped to 0..1. */
10
+ export declare function mixHex(a: string, b: string, t: number): string;
11
+ /** Sample `n` colors evenly across a multi-stop gradient. */
12
+ export declare function gradient(stops: string[], n: number): string[];
13
+ /** Rotate a hex color's hue by `degrees` (HSL space). */
14
+ export declare function hueShift(hex: string, degrees: number): string;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared, design-system-neutral utilities for terminal components: the glyph
3
+ * set the SigX-tui spec standardizes on, interaction timings, and re-exports of
4
+ * the renderer's focus + key APIs so skins depend only on `@sigx/terminal-zero`.
5
+ */
6
+ export { focusState, registerFocusable, unregisterFocusable, focus, focusNext, focusPrev, onKey, setScreenBackground, setScreenForeground, } from '@sigx/runtime-terminal';
7
+ export { hexToSGR, resolveFg, getColorDepth, getOutputTarget, writeStatic, printStatic, displayWidth, truncateToWidth, } from '@sigx/runtime-terminal';
8
+ export { renderTerminal, dispatchKey, type RenderTerminalOptions, type KeyLayer, type KeyHandler, } from '@sigx/runtime-terminal';
9
+ export { charWidth, resolveBg, } from '@sigx/runtime-terminal';
10
+ export { getTerminalSize, syncTerminalSize, } from '@sigx/runtime-terminal';
11
+ export * from './colorMath';
12
+ export * from './ticker';
13
+ export * from './textBuffer';
14
+ export * from './viewStack';
15
+ export { generateQR } from './qr';
16
+ /**
17
+ * Standard glyphs. All are width-1 in monospace fonts (Braille, geometric,
18
+ * box-drawing) — except the status icons, which some terminals render as 2
19
+ * cells; prefer them only where a trailing column is acceptable.
20
+ */
21
+ export declare const GLYPHS: {
22
+ readonly checkboxOn: '\u25C9';
23
+ readonly checkboxOff: '\u25EF';
24
+ readonly radioOn: '\u25CF';
25
+ readonly radioOff: '\u25CB';
26
+ readonly cursor: '\u276F';
27
+ readonly focusBar: '\u258C';
28
+ readonly shadowCell: '\u2592';
29
+ readonly barFull: '\u2588';
30
+ readonly barEmpty: '\u2591';
31
+ readonly barEighths: readonly string[];
32
+ readonly check: '\u2714';
33
+ readonly cross: '\u2716';
34
+ readonly warn: '\u26A0';
35
+ readonly info: '\u2139';
36
+ readonly spinner: readonly string[];
37
+ };
38
+ /**
39
+ * Spinner frame sets, selectable via the Spinner `variant` prop. All width-1
40
+ * except `moon` (emoji — 2 cells; use where a wider glyph column is fine).
41
+ */
42
+ export declare const SPINNERS: {
43
+ readonly dots: readonly string[];
44
+ readonly line: readonly ["—", "\\", "|", "/"];
45
+ readonly arc: readonly ["◜", "◠", "◝", "◞", "◡", "◟"];
46
+ readonly circle: readonly ["◐", "◓", "◑", "◒"];
47
+ readonly bounce: readonly ["▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃"];
48
+ readonly moon: readonly ["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"];
49
+ };
50
+ export type SpinnerVariant = keyof typeof SPINNERS;
51
+ /** Milliseconds a button stays in its visual "pressed" state after activation. */
52
+ export declare const PRESS_MS = 120;
53
+ /** Milliseconds to ignore input after mount (debounces the activating Enter). */
54
+ export declare const READY_DELAY_MS = 50;