@kud/ink-ui 0.1.1 → 0.3.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
@@ -1,284 +1,74 @@
1
1
  <div align="center">
2
2
 
3
- [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
4
- [![Node.js](https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org/)
5
- [![npm](https://img.shields.io/badge/npm-%40kud%2Fink--ui-CB3837?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@kud/ink-ui)
6
- [![MIT](https://img.shields.io/badge/licence-MIT-22C55E?style=flat-square)](./LICENSE)
3
+ ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white)
4
+ ![Node.js](https://img.shields.io/badge/Node.js-339933?style=flat-square&logo=node.js&logoColor=white)
5
+ ![npm](https://img.shields.io/npm/v/%40kud%2Fink-ui?style=flat-square&color=CB3837)
6
+ ![MIT](https://img.shields.io/badge/licence-MIT-22C55E?style=flat-square)
7
7
 
8
- **Opinionated design-system library for Ink CLIs — pre-styled components and design tokens for a consistent terminal UI.**
8
+ **React component library for Ink CLIs**
9
9
 
10
- [Features](#-features) • [Quick Start](#-quick-start) [Components](#-components) • [Tokens](#-design-tokens) • [Development](#-development)
10
+ <a href="https://kud.io/projects/ink-ui">Website</a> · <a href="https://kud.io/projects/ink-ui/docs">Documentation</a>
11
11
 
12
12
  </div>
13
13
 
14
- ---
14
+ ## Features
15
15
 
16
- ## Features
16
+ - **Ten ready-made components** — Banner, Header, Badge, Spinner, Table, Tabs, FooterHints, LoadingScreen, KeyValue, and SelectableRow, pre-styled and ready to drop in
17
+ - **Design tokens included** — a shared colour palette (`colors`) and spacing scale (`spacing`) to keep every screen consistent
18
+ - **Full TypeScript support** — ships compiled output with `.d.ts` declarations for every component and token type
19
+ - **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
17
21
 
18
- - 🧱 **Five ready-made components** — Banner, Header, Badge, Spinner, and Table, all pre-styled and immediately usable
19
- - 🎨 **Design tokens included** — a shared colour palette and spacing scale to keep every screen consistent
20
- - 🟠 **Accent-driven style** — a warm `#FF8C00` accent colour gives your CLI a distinctive, coherent identity
21
- - ⚡ **ESM only, zero config** — ships compiled TypeScript with full type definitions; just import and render
22
- - 🔒 **Peer-dep friendly** — works with any Ink ≥ 4 and React ≥ 18 setup, no version lock-in
23
- - 🧩 **Composable** — every component accepts standard Ink primitives so you can mix them with your own layouts freely
24
- - 🏷 **Typed throughout** — exported types (`BadgeVariant`, `Column`, `Color`, `Spacing`) make props self-documenting in any IDE
25
-
26
- ---
27
-
28
- ## 🚀 Quick Start
29
-
30
- ### Install
22
+ ## Install
31
23
 
32
24
  ```sh
33
25
  npm install @kud/ink-ui
34
26
  ```
35
27
 
36
- `ink` and `react` are peer dependencies add them if you haven't already:
28
+ `ink` and `react` are peer dependencies and must be installed separately:
37
29
 
38
30
  ```sh
39
31
  npm install ink react
40
32
  ```
41
33
 
42
- ### Usage
34
+ ## Usage
43
35
 
44
36
  ```tsx
45
37
  import React from "react"
46
38
  import { render } from "ink"
47
- import { Banner, Header, Badge, Spinner, Table } from "@kud/ink-ui"
39
+ import { Banner, Header, Badge, Spinner, colors } from "@kud/ink-ui"
48
40
 
49
41
  const App = () => (
50
42
  <>
51
- <Banner title="My CLI" subtitle="v1.0.0" icon="◆" />
52
-
53
- <Header subtitle="Fetching packages…">Dependencies</Header>
54
-
55
- <Badge variant="success">installed</Badge>
56
- <Badge variant="warning">outdated</Badge>
57
-
43
+ <Banner title="my-tool" subtitle="v1.0.0" />
44
+ <Header subtitle="Synchronising files…">Status</Header>
45
+ <Badge variant="success">done</Badge>
58
46
  <Spinner label="Loading…" />
59
-
60
- <Table
61
- columns={[
62
- { key: "name", header: "Package", width: 20 },
63
- { key: "version", header: "Version", width: 10 },
64
- ]}
65
- data={[
66
- { name: "ink", version: "5.0.0" },
67
- { name: "react", version: "18.3.1" },
68
- ]}
69
- />
70
47
  </>
71
48
  )
72
49
 
73
50
  render(<App />)
74
51
  ```
75
52
 
76
- Expected output (approximate terminal rendering):
77
-
78
- ```
79
- ◆ My CLI v1.0.0
80
-
81
- Dependencies
82
- Fetching packages…
83
-
84
- [installed] [outdated]
85
-
86
- ⠋ Loading…
87
-
88
- Package Version
89
- ink 5.0.0
90
- react 18.3.1
91
- ```
92
-
93
- ---
94
-
95
- ## 🧩 Components
96
-
97
- ### `<Banner>`
98
-
99
- Full-width title block — ideal as an app header at the top of a screen.
100
-
101
- | Prop | Type | Default | Description |
102
- | ---------- | -------- | ------- | ---------------------------------------- |
103
- | `title` | `string` | — | Bold title text |
104
- | `subtitle` | `string` | — | Optional dimmed subtitle rendered inline |
105
- | `icon` | `string` | `"◆"` | Leading icon rendered in accent colour |
106
-
107
- ```tsx
108
- <Banner title="Deploy Tool" subtitle="production" icon="🚀" />
109
- ```
110
-
111
- ---
112
-
113
- ### `<Header>`
114
-
115
- Bold, underlined section heading. Use it to separate logical groups within a screen.
116
-
117
- | Prop | Type | Default | Description |
118
- | ---------- | -------- | ------- | ------------------------------------------ |
119
- | `children` | `string` | — | The heading text |
120
- | `subtitle` | `string` | — | Optional dimmed subtitle below the heading |
121
-
122
- ```tsx
123
- <Header subtitle="3 items">Results</Header>
124
- ```
125
-
126
- ---
127
-
128
- ### `<Badge>`
129
-
130
- Inline `[label]` tag with semantic colour variants.
131
-
132
- | Prop | Type | Default | Description |
133
- | ---------- | -------------- | -------- | ------------------------------------ |
134
- | `children` | `string` | — | Label text displayed inside brackets |
135
- | `variant` | `BadgeVariant` | `"info"` | Colour variant (see table below) |
136
-
137
- | Variant | Colour |
138
- | --------- | ------ |
139
- | `success` | green |
140
- | `error` | red |
141
- | `warning` | yellow |
142
- | `info` | cyan |
143
-
144
- ```tsx
145
- <Badge variant="error">failed</Badge>
146
- ```
147
-
148
- ---
149
-
150
- ### `<Spinner>`
151
-
152
- Animated dots spinner in accent colour, with an optional dimmed label.
153
-
154
- | Prop | Type | Default | Description |
155
- | ------- | -------- | ------- | ----------------------------------------- |
156
- | `label` | `string` | — | Optional text rendered beside the spinner |
157
-
158
- ```tsx
159
- <Spinner label="Building…" />
160
- ```
161
-
162
- ---
163
-
164
- ### `<Table>`
165
-
166
- Structured data grid with bold muted column headers. Supports optional fixed column widths and truncates long values cleanly.
167
-
168
- | Prop | Type | Description |
169
- | --------- | ------------- | ------------------------------ |
170
- | `data` | `T[]` | Array of row objects |
171
- | `columns` | `Column<T>[]` | Column definitions (see below) |
172
-
173
- **`Column<T>` shape:**
174
-
175
- | Field | Type | Description |
176
- | -------- | --------- | ---------------------------------- |
177
- | `key` | `keyof T` | Object key to read for this column |
178
- | `header` | `string` | Column heading text |
179
- | `width` | `number` | Optional fixed character width |
180
-
181
- ```tsx
182
- <Table
183
- columns={[
184
- { key: "name", header: "Name", width: 16 },
185
- { key: "status", header: "Status" },
186
- ]}
187
- data={[
188
- { name: "api", status: "ok" },
189
- { name: "db", status: "degraded" },
190
- ]}
191
- />
192
- ```
193
-
194
- ---
195
-
196
- ## 🎨 Design Tokens
197
-
198
- Import `colors` and `spacing` to keep your own components aligned with the design system.
53
+ All components accept only the props they need — no theme provider or context required. Design tokens are plain objects:
199
54
 
200
55
  ```ts
201
56
  import { colors, spacing } from "@kud/ink-ui"
202
- ```
203
-
204
- ### Colours
205
-
206
- | Token | Value | Semantic use |
207
- | ---------------- | --------- | ---------------------------------- |
208
- | `colors.accent` | `#FF8C00` | Primary highlight, icons, spinners |
209
- | `colors.muted` | `gray` | Dimmed text, table headers |
210
- | `colors.success` | `green` | Positive states |
211
- | `colors.error` | `red` | Failures and errors |
212
- | `colors.warning` | `yellow` | Caution states |
213
- | `colors.info` | `cyan` | Neutral informational |
214
-
215
- ### Spacing
216
-
217
- | Token | Value | Description |
218
- | ------------ | ----- | ---------------------------------- |
219
- | `spacing.xs` | `1` | Tight gap between inline elements |
220
- | `spacing.sm` | `2` | Small margin between grouped items |
221
- | `spacing.md` | `3` | Standard section margin |
222
- | `spacing.lg` | `4` | Large gap between major sections |
223
57
 
224
- ---
225
-
226
- ## 🔧 Development
227
-
228
- ### Project structure
229
-
230
- ```
231
- ink-ui/
232
- ├── src/
233
- │ ├── components/
234
- │ │ ├── Badge.tsx
235
- │ │ ├── Banner.tsx
236
- │ │ ├── Header.tsx
237
- │ │ ├── Spinner.tsx
238
- │ │ └── Table.tsx
239
- │ ├── index.ts # public API
240
- │ └── tokens.ts # colours + spacing
241
- ├── dist/ # compiled output (git-ignored)
242
- ├── tsconfig.json
243
- └── package.json
58
+ // colors.accent → "#FF8C00"
59
+ // colors.success → "green"
60
+ // spacing.md → 3
244
61
  ```
245
62
 
246
- ### Scripts
247
-
248
- | Script | Command | Description |
249
- | ------- | ------------- | ------------------------------------ |
250
- | `build` | `tsc` | Compile TypeScript to `dist/` |
251
- | `dev` | `tsc --watch` | Watch mode — recompile on every save |
252
-
253
- ### Local setup
63
+ ## Development
254
64
 
255
65
  ```sh
256
66
  git clone https://github.com/kud/ink-ui.git
257
67
  cd ink-ui
258
68
  npm install
259
- npm run build
69
+ npm run dev
260
70
  ```
261
71
 
262
- To develop against a local project, link the package:
263
-
264
- ```sh
265
- npm link
266
- cd ../your-project
267
- npm link @kud/ink-ui
268
- ```
269
-
270
- ---
271
-
272
- ## 🏗 Tech Stack
273
-
274
- | Technology | Purpose |
275
- | ---------------------------------------------------------- | -------------------------------------- |
276
- | [TypeScript](https://www.typescriptlang.org/) | Strict typing, declaration file output |
277
- | [Ink](https://github.com/vadimdemedes/ink) | React renderer for the terminal |
278
- | [React](https://react.dev/) | Component model and JSX |
279
- | [ink-spinner](https://github.com/vadimdemedes/ink-spinner) | Animated spinner primitive |
280
- | ESM | Modern module format, no CommonJS |
281
-
282
- ---
72
+ `npm run build` compiles TypeScript to `dist/`. `npm run dev` runs the compiler in watch mode.
283
73
 
284
- MIT © [kud](https://github.com/kud) — Made with ❤️
74
+ 📚 **Full documentation → [ink-ui/docs](https://kud.io/projects/ink-ui/docs)**
package/dist/index.d.ts CHANGED
@@ -1,10 +1,189 @@
1
- export { Banner } from "./components/Banner.js";
2
- export { Badge } from "./components/Badge.js";
3
- export type { BadgeVariant } from "./components/Badge.js";
4
- export { Header } from "./components/Header.js";
5
- export { Spinner } from "./components/Spinner.js";
6
- export { Table } from "./components/Table.js";
7
- export type { Column } from "./components/Table.js";
8
- export { colors, spacing } from "./tokens.js";
9
- export type { Color, Spacing } from "./tokens.js";
10
- //# sourceMappingURL=index.d.ts.map
1
+ import React from 'react';
2
+
3
+ type BannerProps = {
4
+ title: string;
5
+ subtitle?: string;
6
+ icon?: string;
7
+ };
8
+ declare const Banner: ({ title, subtitle, icon }: BannerProps) => React.JSX.Element;
9
+
10
+ type BadgeVariant = "success" | "error" | "warning" | "info";
11
+ type BadgeProps = {
12
+ children: string;
13
+ variant?: BadgeVariant;
14
+ };
15
+ declare const Badge: ({ children, variant }: BadgeProps) => React.JSX.Element;
16
+
17
+ type HeaderProps = {
18
+ children: string;
19
+ subtitle?: string;
20
+ };
21
+ declare const Header: ({ children, subtitle }: HeaderProps) => React.JSX.Element;
22
+
23
+ type SpinnerProps = {
24
+ label?: string;
25
+ };
26
+ declare const Spinner: ({ label }: SpinnerProps) => React.JSX.Element;
27
+
28
+ type Column<T extends Record<string, unknown>> = {
29
+ key: keyof T & string;
30
+ header: string;
31
+ width?: number;
32
+ };
33
+ type TableProps<T extends Record<string, unknown>> = {
34
+ data: T[];
35
+ columns: Column<T>[];
36
+ };
37
+ declare const Table: <T extends Record<string, unknown>>({ data, columns, }: TableProps<T>) => React.JSX.Element;
38
+
39
+ type Hint = [key: string, label: string];
40
+ type FooterHintsProps = {
41
+ hints: Hint[];
42
+ };
43
+ declare const FooterHints: ({ hints }: FooterHintsProps) => React.JSX.Element;
44
+
45
+ type KeyValueProps = {
46
+ label: string;
47
+ value: React.ReactNode;
48
+ labelWidth?: number;
49
+ };
50
+ declare const KeyValue: ({ label, value, labelWidth }: KeyValueProps) => React.JSX.Element;
51
+
52
+ type LoadingScreenProps = {
53
+ label: string;
54
+ };
55
+ declare const LoadingScreen: ({ label }: LoadingScreenProps) => React.JSX.Element;
56
+
57
+ type SelectableRowProps = {
58
+ active?: boolean;
59
+ marker?: string;
60
+ children: React.ReactNode;
61
+ };
62
+ declare const SelectableRow: ({ active, marker, children, }: SelectableRowProps) => React.JSX.Element;
63
+
64
+ type SelectOption<T extends string = string> = {
65
+ label: string;
66
+ value: T;
67
+ };
68
+ type SelectProps<T extends string> = {
69
+ options: SelectOption<T>[];
70
+ onChange?: (value: T) => void;
71
+ onSubmit?: (value: T) => void;
72
+ defaultValue?: T;
73
+ isDisabled?: boolean;
74
+ visibleOptionCount?: number;
75
+ };
76
+ declare const Select: <T extends string>({ options, onChange, onSubmit, defaultValue, isDisabled, visibleOptionCount, }: SelectProps<T>) => React.JSX.Element;
77
+
78
+ type MultiSelectProps<T extends string> = {
79
+ options: SelectOption<T>[];
80
+ onChange?: (values: T[]) => void;
81
+ onSubmit?: (values: T[]) => void;
82
+ defaultValue?: T[];
83
+ isDisabled?: boolean;
84
+ visibleOptionCount?: number;
85
+ };
86
+ declare const MultiSelect: <T extends string>({ options, onChange, onSubmit, defaultValue, isDisabled, visibleOptionCount, }: MultiSelectProps<T>) => React.JSX.Element;
87
+
88
+ type TextInputProps = {
89
+ placeholder?: string;
90
+ defaultValue?: string;
91
+ onChange?: (value: string) => void;
92
+ onSubmit?: (value: string) => void;
93
+ isDisabled?: boolean;
94
+ };
95
+ declare const TextInput: ({ placeholder, defaultValue, onChange, onSubmit, isDisabled, }: TextInputProps) => React.JSX.Element;
96
+
97
+ type ConfirmInputProps = {
98
+ defaultChoice?: "confirm" | "cancel";
99
+ onConfirm?: () => void;
100
+ onCancel?: () => void;
101
+ isDisabled?: boolean;
102
+ };
103
+ declare const ConfirmInput: ({ defaultChoice, onConfirm, onCancel, isDisabled, }: ConfirmInputProps) => React.JSX.Element;
104
+
105
+ type Span = {
106
+ text: string;
107
+ color?: string;
108
+ bold?: boolean;
109
+ dim?: boolean;
110
+ italic?: boolean;
111
+ };
112
+ type StyledLine = {
113
+ text: string;
114
+ color?: string;
115
+ bold?: boolean;
116
+ dim?: boolean;
117
+ spans?: Span[];
118
+ };
119
+ type ScrollViewProps = {
120
+ lines: StyledLine[];
121
+ showIndicator?: boolean;
122
+ };
123
+ declare const ScrollView: ({ lines, showIndicator, }: ScrollViewProps) => React.JSX.Element;
124
+
125
+ type TabItem<T extends string = string> = {
126
+ value: T;
127
+ label: string;
128
+ count?: number;
129
+ };
130
+ type TabsProps<T extends string> = {
131
+ active: T;
132
+ items: TabItem<T>[];
133
+ };
134
+ declare const Tabs: <T extends string>({ active, items }: TabsProps<T>) => React.JSX.Element;
135
+
136
+ type ProgressBarProps = {
137
+ value: number;
138
+ width?: number;
139
+ };
140
+ declare const ProgressBar: ({ value, width }: ProgressBarProps) => React.JSX.Element;
141
+
142
+ type StatusVariant = "success" | "error" | "warning" | "info";
143
+
144
+ type StatusMessageProps = {
145
+ variant?: StatusVariant;
146
+ children: React.ReactNode;
147
+ };
148
+ declare const StatusMessage: ({ variant, children, }: StatusMessageProps) => React.JSX.Element;
149
+
150
+ type AlertProps = {
151
+ variant?: StatusVariant;
152
+ title?: string;
153
+ children: React.ReactNode;
154
+ };
155
+ declare const Alert: ({ variant, title, children }: AlertProps) => React.JSX.Element;
156
+
157
+ type ToastProps = {
158
+ message: string;
159
+ variant?: StatusVariant;
160
+ duration?: number;
161
+ onDone?: () => void;
162
+ };
163
+ declare const Toast: ({ message, variant, duration, onDone, }: ToastProps) => React.JSX.Element | null;
164
+
165
+ type NotifyOptions = {
166
+ title?: string;
167
+ subtitle?: string;
168
+ sound?: boolean | string;
169
+ };
170
+ declare const notify: (message: string, options?: NotifyOptions) => void;
171
+
172
+ declare const colors: {
173
+ readonly accent: "#FF8C00";
174
+ readonly muted: "gray";
175
+ readonly success: "green";
176
+ readonly error: "red";
177
+ readonly warning: "yellow";
178
+ readonly info: "cyan";
179
+ };
180
+ type Color = (typeof colors)[keyof typeof colors];
181
+ declare const spacing: {
182
+ readonly xs: 1;
183
+ readonly sm: 2;
184
+ readonly md: 3;
185
+ readonly lg: 4;
186
+ };
187
+ type Spacing = (typeof spacing)[keyof typeof spacing];
188
+
189
+ export { Alert, Badge, type BadgeVariant, Banner, type Color, type Column, ConfirmInput, FooterHints, Header, type Hint, KeyValue, LoadingScreen, MultiSelect, type NotifyOptions, ProgressBar, ScrollView, Select, type SelectOption, SelectableRow, type Spacing, type Span, Spinner, StatusMessage, type StatusVariant, type StyledLine, type TabItem, Table, Tabs, TextInput, Toast, colors, notify, spacing };
package/dist/index.js CHANGED
@@ -1,7 +1,414 @@
1
- export { Banner } from "./components/Banner.js";
2
- export { Badge } from "./components/Badge.js";
3
- export { Header } from "./components/Header.js";
4
- export { Spinner } from "./components/Spinner.js";
5
- export { Table } from "./components/Table.js";
6
- export { colors, spacing } from "./tokens.js";
7
- //# sourceMappingURL=index.js.map
1
+ import { Box, Text, useInput, measureElement } from 'ink';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useState, useEffect, useRef, useLayoutEffect } from 'react';
4
+ import cliSpinners from 'cli-spinners';
5
+ import { spawn } from 'child_process';
6
+
7
+ // src/components/Banner.tsx
8
+
9
+ // src/tokens.ts
10
+ var colors = {
11
+ accent: "#FF8C00",
12
+ muted: "gray",
13
+ success: "green",
14
+ error: "red",
15
+ warning: "yellow",
16
+ info: "cyan"
17
+ };
18
+ var spacing = {
19
+ xs: 1,
20
+ sm: 2,
21
+ md: 3,
22
+ lg: 4
23
+ };
24
+ var Banner = ({ title, subtitle, icon = "\u25C6" }) => /* @__PURE__ */ jsxs(Box, { gap: 1, marginBottom: 1, children: [
25
+ /* @__PURE__ */ jsx(Text, { color: colors.accent, children: icon }),
26
+ /* @__PURE__ */ jsx(Text, { bold: true, children: title }),
27
+ subtitle && /* @__PURE__ */ jsx(Text, { dimColor: true, children: subtitle })
28
+ ] });
29
+ var variantColor = {
30
+ success: colors.success,
31
+ error: colors.error,
32
+ warning: colors.warning,
33
+ info: colors.info
34
+ };
35
+ var Badge = ({ children, variant = "info" }) => /* @__PURE__ */ jsxs(Text, { color: variantColor[variant], children: [
36
+ "[",
37
+ children,
38
+ "]"
39
+ ] });
40
+ var Header = ({ children, subtitle }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
41
+ /* @__PURE__ */ jsx(Text, { bold: true, underline: true, children }),
42
+ subtitle && /* @__PURE__ */ jsx(Text, { dimColor: true, children: subtitle })
43
+ ] });
44
+ var dots = cliSpinners.dots;
45
+ var Spinner = ({ label }) => {
46
+ const [frame, setFrame] = useState(0);
47
+ useEffect(() => {
48
+ const timer = setInterval(() => {
49
+ setFrame((prev) => (prev + 1) % dots.frames.length);
50
+ }, dots.interval);
51
+ return () => clearInterval(timer);
52
+ }, []);
53
+ return /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
54
+ /* @__PURE__ */ jsx(Text, { color: colors.accent, children: dots.frames[frame] }),
55
+ label && /* @__PURE__ */ jsx(Text, { dimColor: true, children: label })
56
+ ] });
57
+ };
58
+ var Table = ({
59
+ data,
60
+ columns
61
+ }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
62
+ /* @__PURE__ */ jsx(Box, { gap: 2, children: columns.map((col) => /* @__PURE__ */ jsx(Box, { width: col.width, minWidth: col.width, children: /* @__PURE__ */ jsx(Text, { bold: true, color: colors.muted, children: col.header }) }, col.key)) }),
63
+ data.map((row, i) => /* @__PURE__ */ jsx(Box, { gap: 2, children: columns.map((col) => /* @__PURE__ */ jsx(Box, { width: col.width, minWidth: col.width, children: /* @__PURE__ */ jsx(Text, { wrap: "truncate-end", children: String(row[col.key] ?? "") }) }, col.key)) }, i))
64
+ ] });
65
+ var FooterHints = ({ hints }) => /* @__PURE__ */ jsx(Box, { gap: 2, flexWrap: "wrap", children: hints.map(([key, label]) => /* @__PURE__ */ jsxs(Box, { children: [
66
+ /* @__PURE__ */ jsx(Text, { color: "white", children: key }),
67
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " + label })
68
+ ] }, key)) });
69
+ var isTextValue = (value) => typeof value === "string" || typeof value === "number";
70
+ var KeyValue = ({ label, value, labelWidth = 12 }) => /* @__PURE__ */ jsxs(Box, { children: [
71
+ /* @__PURE__ */ jsx(Box, { width: labelWidth, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: label }) }),
72
+ isTextValue(value) ? /* @__PURE__ */ jsx(Text, { children: value }) : value
73
+ ] });
74
+ var LoadingScreen = ({ label }) => /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(Spinner, { label }) });
75
+ var isTextValue2 = (value) => typeof value === "string" || typeof value === "number";
76
+ var SelectableRow = ({
77
+ active = false,
78
+ marker = "\u276F",
79
+ children
80
+ }) => /* @__PURE__ */ jsxs(Box, { children: [
81
+ /* @__PURE__ */ jsx(Text, { color: colors.info, children: active ? ` ${marker} ` : " " }),
82
+ isTextValue2(children) ? /* @__PURE__ */ jsx(Text, { bold: active, children }) : children
83
+ ] });
84
+ var windowStart = (cursor, total, count) => {
85
+ if (total <= count) return 0;
86
+ return Math.max(0, Math.min(cursor - Math.floor(count / 2), total - count));
87
+ };
88
+ var Select = ({
89
+ options,
90
+ onChange,
91
+ onSubmit,
92
+ defaultValue,
93
+ isDisabled = false,
94
+ visibleOptionCount
95
+ }) => {
96
+ const initial = options.findIndex((o) => o.value === defaultValue);
97
+ const [cursor, setCursor] = useState(initial === -1 ? 0 : initial);
98
+ const move = (delta) => {
99
+ const next = (cursor + delta + options.length) % options.length;
100
+ setCursor(next);
101
+ onChange?.(options[next].value);
102
+ };
103
+ useInput(
104
+ (_input, key) => {
105
+ if (key.upArrow) move(-1);
106
+ if (key.downArrow) move(1);
107
+ if (key.return) onSubmit?.(options[cursor].value);
108
+ },
109
+ { isActive: !isDisabled && options.length > 0 }
110
+ );
111
+ const count = visibleOptionCount ?? options.length;
112
+ const start = windowStart(cursor, options.length, count);
113
+ const visible = options.slice(start, start + count);
114
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
115
+ visible.map((option, i) => {
116
+ const active = start + i === cursor;
117
+ return /* @__PURE__ */ jsx(SelectableRow, { active, children: /* @__PURE__ */ jsx(Text, { bold: active, color: active ? colors.accent : void 0, children: option.label }) }, option.value);
118
+ }),
119
+ options.length > count && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2026 ${options.length} total` })
120
+ ] });
121
+ };
122
+ var windowStart2 = (cursor, total, count) => {
123
+ if (total <= count) return 0;
124
+ return Math.max(0, Math.min(cursor - Math.floor(count / 2), total - count));
125
+ };
126
+ var MultiSelect = ({
127
+ options,
128
+ onChange,
129
+ onSubmit,
130
+ defaultValue = [],
131
+ isDisabled = false,
132
+ visibleOptionCount
133
+ }) => {
134
+ const [cursor, setCursor] = useState(0);
135
+ const [selected, setSelected] = useState(new Set(defaultValue));
136
+ useInput(
137
+ (input, key) => {
138
+ if (key.upArrow)
139
+ setCursor((i) => (i - 1 + options.length) % options.length);
140
+ if (key.downArrow) setCursor((i) => (i + 1) % options.length);
141
+ if (input === " ") {
142
+ const value = options[cursor].value;
143
+ const next = new Set(selected);
144
+ if (next.has(value)) next.delete(value);
145
+ else next.add(value);
146
+ setSelected(next);
147
+ onChange?.([...next]);
148
+ }
149
+ if (key.return) onSubmit?.([...selected]);
150
+ },
151
+ { isActive: !isDisabled && options.length > 0 }
152
+ );
153
+ const count = visibleOptionCount ?? options.length;
154
+ const start = windowStart2(cursor, options.length, count);
155
+ const visible = options.slice(start, start + count);
156
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
157
+ visible.map((option, i) => {
158
+ const active = start + i === cursor;
159
+ const on = selected.has(option.value);
160
+ return /* @__PURE__ */ jsxs(SelectableRow, { active, children: [
161
+ /* @__PURE__ */ jsx(Text, { color: on ? colors.success : void 0, children: on ? "\u25C9" : "\u25CB" }),
162
+ /* @__PURE__ */ jsx(Text, { bold: active, color: active ? colors.accent : void 0, children: " " + option.label })
163
+ ] }, option.value);
164
+ }),
165
+ options.length > count && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2026 ${options.length} total` })
166
+ ] });
167
+ };
168
+ var TextInput = ({
169
+ placeholder = "",
170
+ defaultValue = "",
171
+ onChange,
172
+ onSubmit,
173
+ isDisabled = false
174
+ }) => {
175
+ const [value, setValue] = useState(defaultValue);
176
+ const [cursor, setCursor] = useState(defaultValue.length);
177
+ const update = (nextValue, nextCursor) => {
178
+ setValue(nextValue);
179
+ setCursor(nextCursor);
180
+ onChange?.(nextValue);
181
+ };
182
+ useInput(
183
+ (input, key) => {
184
+ if (key.return) {
185
+ onSubmit?.(value);
186
+ return;
187
+ }
188
+ if (key.leftArrow) {
189
+ setCursor((c) => Math.max(0, c - 1));
190
+ return;
191
+ }
192
+ if (key.rightArrow) {
193
+ setCursor((c) => Math.min(value.length, c + 1));
194
+ return;
195
+ }
196
+ if (key.backspace || key.delete) {
197
+ if (cursor === 0) return;
198
+ update(value.slice(0, cursor - 1) + value.slice(cursor), cursor - 1);
199
+ return;
200
+ }
201
+ if (input && !key.ctrl && !key.meta && !key.tab) {
202
+ update(
203
+ value.slice(0, cursor) + input + value.slice(cursor),
204
+ cursor + input.length
205
+ );
206
+ }
207
+ },
208
+ { isActive: !isDisabled }
209
+ );
210
+ if (value.length === 0) {
211
+ return /* @__PURE__ */ jsxs(Text, { children: [
212
+ /* @__PURE__ */ jsx(Text, { inverse: true, children: " " }),
213
+ placeholder ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: placeholder }) : null
214
+ ] });
215
+ }
216
+ const before = value.slice(0, cursor);
217
+ const at = value.slice(cursor, cursor + 1) || " ";
218
+ const after = value.slice(cursor + 1);
219
+ return /* @__PURE__ */ jsxs(Text, { children: [
220
+ before,
221
+ /* @__PURE__ */ jsx(Text, { inverse: true, children: at }),
222
+ after
223
+ ] });
224
+ };
225
+ var ConfirmInput = ({
226
+ defaultChoice = "confirm",
227
+ onConfirm,
228
+ onCancel,
229
+ isDisabled = false
230
+ }) => {
231
+ useInput(
232
+ (input, key) => {
233
+ if (input.toLowerCase() === "y") {
234
+ onConfirm?.();
235
+ return;
236
+ }
237
+ if (input.toLowerCase() === "n") {
238
+ onCancel?.();
239
+ return;
240
+ }
241
+ if (key.return) {
242
+ if (defaultChoice === "confirm") onConfirm?.();
243
+ else onCancel?.();
244
+ }
245
+ },
246
+ { isActive: !isDisabled }
247
+ );
248
+ const hint = defaultChoice === "confirm" ? "(Y/n)" : "(y/N)";
249
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint });
250
+ };
251
+ var ScrollView = ({
252
+ lines,
253
+ showIndicator = true
254
+ }) => {
255
+ const ref = useRef(null);
256
+ const [height, setHeight] = useState(1);
257
+ const [start, setStart] = useState(0);
258
+ useLayoutEffect(() => {
259
+ if (!ref.current) return;
260
+ const { height: h } = measureElement(ref.current);
261
+ if (h > 0 && h !== height) setHeight(h);
262
+ });
263
+ const maxStart = Math.max(0, lines.length - height);
264
+ const clamped = Math.min(start, maxStart);
265
+ useInput((input, key) => {
266
+ if (key.downArrow || input === "j")
267
+ setStart((s) => Math.min(maxStart, s + 1));
268
+ if (key.upArrow || input === "k") setStart((s) => Math.max(0, s - 1));
269
+ if (input === " " || key.pageDown || input === "f")
270
+ setStart((s) => Math.min(maxStart, s + height));
271
+ if (key.pageUp || input === "b") setStart((s) => Math.max(0, s - height));
272
+ if (input === "g") setStart(0);
273
+ if (input === "G") setStart(maxStart);
274
+ });
275
+ const visible = lines.slice(clamped, clamped + height);
276
+ const hasMore = lines.length > height;
277
+ const atEnd = clamped >= maxStart;
278
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [
279
+ /* @__PURE__ */ jsx(Box, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: visible.map(
280
+ (l, i) => l.spans ? /* @__PURE__ */ jsxs(Text, { wrap: "truncate-end", children: [
281
+ l.spans.map((s, j) => /* @__PURE__ */ jsx(
282
+ Text,
283
+ {
284
+ color: s.color,
285
+ bold: s.bold,
286
+ dimColor: s.dim,
287
+ italic: s.italic,
288
+ children: s.text
289
+ },
290
+ j
291
+ )),
292
+ l.spans.length === 0 ? " " : ""
293
+ ] }, i) : /* @__PURE__ */ jsx(
294
+ Text,
295
+ {
296
+ color: l.color,
297
+ bold: l.bold,
298
+ dimColor: l.dim,
299
+ wrap: "truncate-end",
300
+ children: l.text || " "
301
+ },
302
+ i
303
+ )
304
+ ) }),
305
+ showIndicator && hasMore && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${clamped + 1}\u2013${clamped + visible.length} / ${lines.length}` + (atEnd ? " (end)" : " \u2193 j/space") })
306
+ ] });
307
+ };
308
+ var Tabs = ({ active, items }) => {
309
+ const cells = items.map((item) => ({
310
+ key: item.value,
311
+ text: item.count !== void 0 ? `${item.label} (${item.count})` : item.label,
312
+ isActive: item.value === active
313
+ }));
314
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
315
+ /* @__PURE__ */ jsx(Box, { gap: 2, children: cells.map((cell) => /* @__PURE__ */ jsx(
316
+ Text,
317
+ {
318
+ bold: cell.isActive,
319
+ color: cell.isActive ? colors.accent : void 0,
320
+ dimColor: !cell.isActive,
321
+ children: cell.text
322
+ },
323
+ cell.key
324
+ )) }),
325
+ /* @__PURE__ */ jsx(Box, { gap: 2, children: cells.map((cell) => /* @__PURE__ */ jsx(Text, { color: colors.accent, children: cell.isActive ? "\u2500".repeat(cell.text.length) : " ".repeat(cell.text.length) }, cell.key)) })
326
+ ] });
327
+ };
328
+ var ProgressBar = ({ value, width = 20 }) => {
329
+ const clamped = Math.max(0, Math.min(100, value));
330
+ const filled = Math.round(clamped / 100 * width);
331
+ return /* @__PURE__ */ jsxs(Text, { children: [
332
+ /* @__PURE__ */ jsx(Text, { color: colors.accent, children: "\u2588".repeat(filled) }),
333
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2591".repeat(width - filled) })
334
+ ] });
335
+ };
336
+
337
+ // src/components/status-variants.ts
338
+ var STATUS_VARIANTS = {
339
+ success: { icon: "\u2713", color: colors.success },
340
+ error: { icon: "\u2717", color: colors.error },
341
+ warning: { icon: "\u26A0", color: colors.warning },
342
+ info: { icon: "\u2139", color: colors.info }
343
+ };
344
+ var StatusMessage = ({
345
+ variant = "info",
346
+ children
347
+ }) => {
348
+ const { icon, color } = STATUS_VARIANTS[variant];
349
+ return /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
350
+ /* @__PURE__ */ jsx(Text, { color, children: icon }),
351
+ /* @__PURE__ */ jsx(Text, { children })
352
+ ] });
353
+ };
354
+ var Alert = ({ variant = "info", title, children }) => {
355
+ const { icon, color } = STATUS_VARIANTS[variant];
356
+ return /* @__PURE__ */ jsxs(
357
+ Box,
358
+ {
359
+ borderStyle: "round",
360
+ borderColor: color,
361
+ paddingX: 1,
362
+ flexDirection: "column",
363
+ children: [
364
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
365
+ /* @__PURE__ */ jsx(Text, { color, children: icon }),
366
+ title ? /* @__PURE__ */ jsx(Text, { bold: true, color, children: title }) : null
367
+ ] }),
368
+ /* @__PURE__ */ jsx(Text, { children })
369
+ ]
370
+ }
371
+ );
372
+ };
373
+ var Toast = ({
374
+ message,
375
+ variant = "info",
376
+ duration = 2e3,
377
+ onDone
378
+ }) => {
379
+ const [visible, setVisible] = useState(true);
380
+ useEffect(() => {
381
+ const timer = setTimeout(() => {
382
+ setVisible(false);
383
+ onDone?.();
384
+ }, duration);
385
+ return () => clearTimeout(timer);
386
+ }, [duration]);
387
+ if (!visible) return null;
388
+ const { icon, color } = STATUS_VARIANTS[variant];
389
+ return /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
390
+ /* @__PURE__ */ jsx(Text, { color, children: icon }),
391
+ /* @__PURE__ */ jsx(Text, { children: message })
392
+ ] });
393
+ };
394
+ var escape = (value) => value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
395
+ var buildAppleScript = (message, options = {}) => {
396
+ const clauses = [
397
+ options.title && `title "${escape(options.title)}"`,
398
+ options.subtitle && `subtitle "${escape(options.subtitle)}"`,
399
+ options.sound && `sound name "${escape(typeof options.sound === "string" ? options.sound : "default")}"`
400
+ ].filter(Boolean);
401
+ const suffix = clauses.length > 0 ? ` with ${clauses.join(" ")}` : "";
402
+ return `display notification "${escape(message)}"${suffix}`;
403
+ };
404
+ var notify = (message, options = {}) => {
405
+ if (process.platform !== "darwin") return;
406
+ const child = spawn("osascript", ["-e", buildAppleScript(message, options)], {
407
+ stdio: "ignore"
408
+ });
409
+ child.on("error", () => {
410
+ });
411
+ child.unref();
412
+ };
413
+
414
+ export { Alert, Badge, Banner, ConfirmInput, FooterHints, Header, KeyValue, LoadingScreen, MultiSelect, ProgressBar, ScrollView, Select, SelectableRow, Spinner, StatusMessage, Table, Tabs, TextInput, Toast, colors, notify, spacing };
package/package.json CHANGED
@@ -1,35 +1,47 @@
1
1
  {
2
2
  "name": "@kud/ink-ui",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "React component library for Ink CLIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
12
  }
13
13
  },
14
14
  "files": [
15
15
  "dist"
16
16
  ],
17
+ "engines": {
18
+ "node": ">=20"
19
+ },
17
20
  "scripts": {
18
- "build": "tsc",
19
- "dev": "tsc --watch"
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest"
20
26
  },
21
27
  "peerDependencies": {
22
- "ink": ">=4.0.0",
23
- "react": ">=18.0.0"
28
+ "ink": ">=7",
29
+ "react": ">=19"
24
30
  },
25
31
  "dependencies": {
26
- "ink-spinner": "^5.0.0"
32
+ "cli-spinners": "3.4.0",
33
+ "figures": "6.1.0"
27
34
  },
28
35
  "devDependencies": {
29
- "@types/react": "^18.0.0",
30
- "ink": "^5.0.0",
31
- "react": "^18.0.0",
32
- "typescript": "^5.0.0"
36
+ "@types/node": "22.20.1",
37
+ "@types/react": "19.2.17",
38
+ "ink": "7.1.0",
39
+ "ink-testing-library": "4.0.0",
40
+ "react": "19.2.7",
41
+ "react-devtools-core": "7.0.1",
42
+ "tsup": "8.5.1",
43
+ "typescript": "5.9.3",
44
+ "vitest": "4.1.10"
33
45
  },
34
46
  "keywords": [
35
47
  "ink",
@@ -1,8 +0,0 @@
1
- export type BadgeVariant = "success" | "error" | "warning" | "info";
2
- type BadgeProps = {
3
- children: string;
4
- variant?: BadgeVariant;
5
- };
6
- export declare const Badge: ({ children, variant }: BadgeProps) => import("react/jsx-runtime").JSX.Element;
7
- export {};
8
- //# sourceMappingURL=Badge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../src/components/Badge.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;AAEnE,KAAK,UAAU,GAAG;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AASD,eAAO,MAAM,KAAK,GAAI,uBAAgC,UAAU,4CAE/D,CAAA"}
@@ -1,11 +0,0 @@
1
- import { jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Text } from "ink";
3
- import { colors } from "../tokens.js";
4
- const variantColor = {
5
- success: colors.success,
6
- error: colors.error,
7
- warning: colors.warning,
8
- info: colors.info,
9
- };
10
- export const Badge = ({ children, variant = "info" }) => (_jsxs(Text, { color: variantColor[variant], children: ["[", children, "]"] }));
11
- //# sourceMappingURL=Badge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Badge.js","sourceRoot":"","sources":["../../src/components/Badge.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AASrC,MAAM,YAAY,GAAiC;IACjD,OAAO,EAAE,MAAM,CAAC,OAAO;IACvB,KAAK,EAAE,MAAM,CAAC,KAAK;IACnB,OAAO,EAAE,MAAM,CAAC,OAAO;IACvB,IAAI,EAAE,MAAM,CAAC,IAAI;CAClB,CAAA;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAc,EAAE,EAAE,CAAC,CACnE,MAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,kBAAI,QAAQ,SAAS,CACxD,CAAA"}
@@ -1,8 +0,0 @@
1
- type BannerProps = {
2
- title: string;
3
- subtitle?: string;
4
- icon?: string;
5
- };
6
- export declare const Banner: ({ title, subtitle, icon }: BannerProps) => import("react/jsx-runtime").JSX.Element;
7
- export {};
8
- //# sourceMappingURL=Banner.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/components/Banner.tsx"],"names":[],"mappings":"AAIA,KAAK,WAAW,GAAG;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,eAAO,MAAM,MAAM,GAAI,2BAAiC,WAAW,4CAMlE,CAAA"}
@@ -1,5 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from "ink";
3
- import { colors } from "../tokens.js";
4
- export const Banner = ({ title, subtitle, icon = "◆" }) => (_jsxs(Box, { gap: 1, marginBottom: 1, children: [_jsx(Text, { color: colors.accent, children: icon }), _jsx(Text, { bold: true, children: title }), subtitle && _jsx(Text, { dimColor: true, children: subtitle })] }));
5
- //# sourceMappingURL=Banner.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Banner.js","sourceRoot":"","sources":["../../src/components/Banner.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAQrC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,GAAG,EAAe,EAAE,EAAE,CAAC,CACtE,MAAC,GAAG,IAAC,GAAG,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,aAC1B,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,YAAG,IAAI,GAAQ,EACzC,KAAC,IAAI,IAAC,IAAI,kBAAE,KAAK,GAAQ,EACxB,QAAQ,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,QAAQ,GAAQ,IACzC,CACP,CAAA"}
@@ -1,7 +0,0 @@
1
- type HeaderProps = {
2
- children: string;
3
- subtitle?: string;
4
- };
5
- export declare const Header: ({ children, subtitle }: HeaderProps) => import("react/jsx-runtime").JSX.Element;
6
- export {};
7
- //# sourceMappingURL=Header.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../src/components/Header.tsx"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,MAAM,GAAI,wBAAwB,WAAW,4CAOzD,CAAA"}
@@ -1,4 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from "ink";
3
- export const Header = ({ children, subtitle }) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, underline: true, children: children }), subtitle && _jsx(Text, { dimColor: true, children: subtitle })] }));
4
- //# sourceMappingURL=Header.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Header.js","sourceRoot":"","sources":["../../src/components/Header.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAO/B,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAe,EAAE,EAAE,CAAC,CAC7D,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,YAAY,EAAE,CAAC,aACzC,KAAC,IAAI,IAAC,IAAI,QAAC,SAAS,kBACjB,QAAQ,GACJ,EACN,QAAQ,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,QAAQ,GAAQ,IACzC,CACP,CAAA"}
@@ -1,6 +0,0 @@
1
- type SpinnerProps = {
2
- label?: string;
3
- };
4
- export declare const Spinner: ({ label }: SpinnerProps) => import("react/jsx-runtime").JSX.Element;
5
- export {};
6
- //# sourceMappingURL=Spinner.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAKA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,OAAO,GAAI,WAAW,YAAY,4CAO9C,CAAA"}
@@ -1,6 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from "ink";
3
- import InkSpinner from "ink-spinner";
4
- import { colors } from "../tokens.js";
5
- export const Spinner = ({ label }) => (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: colors.accent, children: _jsx(InkSpinner, { type: "dots" }) }), label && _jsx(Text, { dimColor: true, children: label })] }));
6
- //# sourceMappingURL=Spinner.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,UAAU,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAMrC,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EAAE,KAAK,EAAgB,EAAE,EAAE,CAAC,CAClD,MAAC,GAAG,IAAC,GAAG,EAAE,CAAC,aACT,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,YACxB,KAAC,UAAU,IAAC,IAAI,EAAC,MAAM,GAAG,GACrB,EACN,KAAK,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,KAAK,GAAQ,IACnC,CACP,CAAA"}
@@ -1,12 +0,0 @@
1
- export type Column<T extends Record<string, unknown>> = {
2
- key: keyof T & string;
3
- header: string;
4
- width?: number;
5
- };
6
- type TableProps<T extends Record<string, unknown>> = {
7
- data: T[];
8
- columns: Column<T>[];
9
- };
10
- export declare const Table: <T extends Record<string, unknown>>({ data, columns, }: TableProps<T>) => import("react/jsx-runtime").JSX.Element;
11
- export {};
12
- //# sourceMappingURL=Table.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../src/components/Table.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACtD,GAAG,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACnD,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;CACrB,CAAA;AAED,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,oBAGtD,UAAU,CAAC,CAAC,CAAC,4CAqBf,CAAA"}
@@ -1,5 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from "ink";
3
- import { colors } from "../tokens.js";
4
- export const Table = ({ data, columns, }) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { gap: 2, children: columns.map((col) => (_jsx(Box, { width: col.width, minWidth: col.width, children: _jsx(Text, { bold: true, color: colors.muted, children: col.header }) }, col.key))) }), data.map((row, i) => (_jsx(Box, { gap: 2, children: columns.map((col) => (_jsx(Box, { width: col.width, minWidth: col.width, children: _jsx(Text, { wrap: "truncate-end", children: String(row[col.key] ?? "") }) }, col.key))) }, i)))] }));
5
- //# sourceMappingURL=Table.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Table.js","sourceRoot":"","sources":["../../src/components/Table.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAarC,MAAM,CAAC,MAAM,KAAK,GAAG,CAAoC,EACvD,IAAI,EACJ,OAAO,GACO,EAAE,EAAE,CAAC,CACnB,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,GAAG,IAAC,GAAG,EAAE,CAAC,YACR,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,KAAC,GAAG,IAAe,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,KAAK,YACtD,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,MAAM,CAAC,KAAK,YAC3B,GAAG,CAAC,MAAM,GACN,IAHC,GAAG,CAAC,GAAG,CAIX,CACP,CAAC,GACE,EACL,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CACpB,KAAC,GAAG,IAAS,GAAG,EAAE,CAAC,YAChB,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,KAAC,GAAG,IAAe,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,KAAK,YACtD,KAAC,IAAI,IAAC,IAAI,EAAC,cAAc,YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAQ,IADrD,GAAG,CAAC,GAAG,CAEX,CACP,CAAC,IALM,CAAC,CAML,CACP,CAAC,IACE,CACP,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAC7C,YAAY,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC7C,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAE7C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAE7C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA"}
package/dist/tokens.d.ts DELETED
@@ -1,17 +0,0 @@
1
- export declare const colors: {
2
- readonly accent: "#FF8C00";
3
- readonly muted: "gray";
4
- readonly success: "green";
5
- readonly error: "red";
6
- readonly warning: "yellow";
7
- readonly info: "cyan";
8
- };
9
- export type Color = (typeof colors)[keyof typeof colors];
10
- export declare const spacing: {
11
- readonly xs: 1;
12
- readonly sm: 2;
13
- readonly md: 3;
14
- readonly lg: 4;
15
- };
16
- export type Spacing = (typeof spacing)[keyof typeof spacing];
17
- //# sourceMappingURL=tokens.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;;;;;;;CAOT,CAAA;AAEV,MAAM,MAAM,KAAK,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAA;AAExD,eAAO,MAAM,OAAO;;;;;CAKV,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAA"}
package/dist/tokens.js DELETED
@@ -1,15 +0,0 @@
1
- export const colors = {
2
- accent: "#FF8C00",
3
- muted: "gray",
4
- success: "green",
5
- error: "red",
6
- warning: "yellow",
7
- info: "cyan",
8
- };
9
- export const spacing = {
10
- xs: 1,
11
- sm: 2,
12
- md: 3,
13
- lg: 4,
14
- };
15
- //# sourceMappingURL=tokens.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,MAAM;IACb,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,QAAQ;IACjB,IAAI,EAAE,MAAM;CACJ,CAAA;AAIV,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;CACG,CAAA"}