@kud/ink-ui 0.7.0 → 0.8.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/README.md CHANGED
@@ -13,11 +13,18 @@
13
13
 
14
14
  ## Features
15
15
 
16
- - **Ten ready-made components** — Banner, Header, Badge, Spinner, Table, Tabs, FooterHints, LoadingScreen, KeyValue, and SelectableRow, pre-styled and ready to drop in
16
+ - **25 ready-made components**, pre-styled and ready to drop in:
17
+ - **Inputs** — `TextInput`, `EmailInput` (domain completion), `PasswordInput` (masked), `ConfirmInput`
18
+ - **Lists** — `UnorderedList`, `OrderedList` (both nestable), `Table`
19
+ - **Selection & navigation** — `Select`, `MultiSelect`, `Tabs`, `Switch`, `Toggle`, `SelectableRow`
20
+ - **Status & feedback** — `Spinner`, `ProgressBar`, `StatusMessage`, `Alert`, `Badge`, `Toast`
21
+ - **Layout & chrome** — `Banner`, `Header`, `FooterHints`, `KeyValue`, `LoadingScreen`, `ScrollView`
22
+ - **Full [@inkjs/ui](https://github.com/vadimdemedes/ink-ui) parity** — every upstream component has an equivalent, plus a dozen more the design system adds on top
23
+ - **Colourblind-safe by design** — state is signalled by shape, case, and glyph, never colour alone
17
24
  - **Design tokens included** — a shared colour palette (`colors`) and spacing scale (`spacing`) to keep every screen consistent
18
25
  - **Full TypeScript support** — ships compiled output with `.d.ts` declarations for every component and token type
19
26
  - **ESM only, zero config** — no runtime bundling step; just import and render
20
- - **Peer-dependency light** — only requires `ink ≥ 4` and `react ≥ 18`; `ink-spinner` is the sole production dependency
27
+ - **Peer-dependency light** — only requires `ink ≥ 7` and `react ≥ 19`
21
28
 
22
29
  ## Install
23
30
 
@@ -50,6 +57,33 @@ const App = () => (
50
57
  render(<App />)
51
58
  ```
52
59
 
60
+ Inputs report their value through `onChange`/`onSubmit`, and lists nest with a shape-distinct marker per level:
61
+
62
+ ```tsx
63
+ import { Text } from "ink"
64
+ import { EmailInput, PasswordInput, UnorderedList } from "@kud/ink-ui"
65
+
66
+ const SignUp = () => (
67
+ <>
68
+ <EmailInput placeholder="you@example.com" onSubmit={setEmail} />
69
+ <PasswordInput placeholder="Password" onSubmit={setPassword} />
70
+ <UnorderedList>
71
+ <UnorderedList.Item>
72
+ <Text>Choose a plan</Text>
73
+ <UnorderedList>
74
+ <UnorderedList.Item>
75
+ <Text>Free</Text>
76
+ </UnorderedList.Item>
77
+ <UnorderedList.Item>
78
+ <Text>Pro</Text>
79
+ </UnorderedList.Item>
80
+ </UnorderedList>
81
+ </UnorderedList.Item>
82
+ </UnorderedList>
83
+ </>
84
+ )
85
+ ```
86
+
53
87
  All components accept only the props they need — no theme provider or context required. Design tokens are plain objects:
54
88
 
55
89
  ```ts
package/dist/index.d.ts CHANGED
@@ -160,8 +160,9 @@ type ScrollViewProps = {
160
160
  lines: StyledLine[];
161
161
  showIndicator?: boolean;
162
162
  initialStart?: number;
163
+ isActive?: boolean;
163
164
  };
164
- declare const ScrollView: ({ lines, showIndicator, initialStart, }: ScrollViewProps) => React.JSX.Element;
165
+ declare const ScrollView: ({ lines, showIndicator, initialStart, isActive, }: ScrollViewProps) => React.JSX.Element;
165
166
 
166
167
  type TabItem<T extends string = string> = {
167
168
  value: T;
package/dist/index.js CHANGED
@@ -115,7 +115,10 @@ var Select = ({
115
115
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
116
116
  visible.map((option, i) => {
117
117
  const active = start + i === cursor;
118
- return /* @__PURE__ */ jsx(SelectableRow, { active, children: /* @__PURE__ */ jsx(Text, { bold: active, color: active ? colors.accent : void 0, children: option.label }) }, option.value);
118
+ return /* @__PURE__ */ jsxs(SelectableRow, { active, children: [
119
+ /* @__PURE__ */ jsx(Text, { bold: active, color: active ? colors.info : void 0, children: option.label }),
120
+ active ? /* @__PURE__ */ jsx(Text, { color: colors.success, children: " \u2713" }) : null
121
+ ] }, option.value);
119
122
  }),
120
123
  options.length > count && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2026 ${options.length} total` })
121
124
  ] });
@@ -159,8 +162,8 @@ var MultiSelect = ({
159
162
  const active = start + i === cursor;
160
163
  const on = selected.has(option.value);
161
164
  return /* @__PURE__ */ jsxs(SelectableRow, { active, children: [
162
- /* @__PURE__ */ jsx(Text, { color: on ? colors.success : void 0, children: on ? "\u25C9" : "\u25CB" }),
163
- /* @__PURE__ */ jsx(Text, { bold: active, color: active ? colors.accent : void 0, children: " " + option.label })
165
+ /* @__PURE__ */ jsx(Text, { bold: active, color: active ? colors.info : void 0, children: option.label }),
166
+ on ? /* @__PURE__ */ jsx(Text, { color: colors.success, children: " \u2713" }) : null
164
167
  ] }, option.value);
165
168
  }),
166
169
  options.length > count && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2026 ${options.length} total` })
@@ -437,7 +440,8 @@ var OrderedList = Object.assign(OrderedListRoot, {
437
440
  var ScrollView = ({
438
441
  lines,
439
442
  showIndicator = true,
440
- initialStart = 0
443
+ initialStart = 0,
444
+ isActive = true
441
445
  }) => {
442
446
  const ref = useRef(null);
443
447
  const [height, setHeight] = useState(1);
@@ -450,16 +454,19 @@ var ScrollView = ({
450
454
  });
451
455
  const maxStart = Math.max(0, lines.length - height);
452
456
  const clamped = Math.min(start, maxStart);
453
- useInput((input, key) => {
454
- if (key.downArrow || input === "j")
455
- setStart((s) => Math.min(maxStart, s + 1));
456
- if (key.upArrow || input === "k") setStart((s) => Math.max(0, s - 1));
457
- if (input === " " || key.pageDown || input === "f")
458
- setStart((s) => Math.min(maxStart, s + height));
459
- if (key.pageUp || input === "b") setStart((s) => Math.max(0, s - height));
460
- if (input === "g") setStart(0);
461
- if (input === "G") setStart(maxStart);
462
- });
457
+ useInput(
458
+ (input, key) => {
459
+ if (key.downArrow || input === "j")
460
+ setStart((s) => Math.min(maxStart, s + 1));
461
+ if (key.upArrow || input === "k") setStart((s) => Math.max(0, s - 1));
462
+ if (input === " " || key.pageDown || input === "f")
463
+ setStart((s) => Math.min(maxStart, s + height));
464
+ if (key.pageUp || input === "b") setStart((s) => Math.max(0, s - height));
465
+ if (input === "g") setStart(0);
466
+ if (input === "G") setStart(maxStart);
467
+ },
468
+ { isActive }
469
+ );
463
470
  const visible = lines.slice(clamped, clamped + height);
464
471
  const hasMore = lines.length > height;
465
472
  const atEnd = clamped >= maxStart;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/ink-ui",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "React component library for Ink CLIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,6 +20,7 @@
20
20
  "scripts": {
21
21
  "build": "tsup",
22
22
  "dev": "tsup --watch",
23
+ "demo": "tsx src/demo/index.tsx",
23
24
  "typecheck": "tsc --noEmit",
24
25
  "test": "vitest run",
25
26
  "test:watch": "vitest"
@@ -41,6 +42,7 @@
41
42
  "react": "19.2.7",
42
43
  "react-devtools-core": "7.0.1",
43
44
  "tsup": "8.5.1",
45
+ "tsx": "4.23.1",
44
46
  "typescript": "5.9.3",
45
47
  "vitest": "4.1.10"
46
48
  },