@runtypelabs/ink-components 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -34,18 +34,34 @@ interface ErrorDisplayProps {
34
34
  declare function ErrorDisplay({ error, context }: ErrorDisplayProps): react_jsx_runtime.JSX.Element;
35
35
 
36
36
  declare const theme: {
37
- readonly error: "red";
38
- readonly success: "green";
39
- readonly warning: "yellow";
40
- readonly info: "cyan";
41
- readonly muted: "gray";
42
- readonly primary: "cyan";
43
- readonly secondary: "blue";
44
- readonly spinner: "green";
45
- readonly progress: "cyan";
46
- readonly label: "white";
47
- readonly dimLabel: "gray";
37
+ readonly background: "#1b212b";
38
+ readonly surface: "#202733";
39
+ readonly surfaceMuted: "#1d2430";
40
+ readonly surfaceElevated: "#252d3b";
41
+ readonly text: "#d8d3c9";
42
+ readonly textMuted: "#918b84";
43
+ readonly textSubtle: "#66625d";
44
+ readonly accent: "#8fc4bf";
45
+ readonly accentActive: "#a7d8d2";
46
+ readonly success: "#8ecb73";
47
+ readonly warning: "#d8ae63";
48
+ readonly danger: "#c97b73";
49
+ readonly border: "#394252";
50
+ readonly borderActive: "#546077";
51
+ readonly error: "#c97b73";
52
+ readonly info: "#8fc4bf";
53
+ readonly muted: "#918b84";
54
+ readonly primary: "#8fc4bf";
55
+ readonly secondary: "#a7d8d2";
56
+ readonly spinner: "#8fc4bf";
57
+ readonly progress: "#8fc4bf";
58
+ readonly label: "#d8d3c9";
59
+ readonly dimLabel: "#66625d";
48
60
  readonly highlight: "bold";
61
+ readonly separator: " · ";
62
+ readonly sectionGap: 1;
63
+ readonly panelPaddingX: 1;
64
+ readonly panelPaddingY: 0;
49
65
  };
50
66
  type Theme = typeof theme;
51
67
 
package/dist/index.js CHANGED
@@ -4,18 +4,34 @@ import InkSpinner from "ink-spinner";
4
4
 
5
5
  // src/theme.ts
6
6
  var theme = {
7
- error: "red",
8
- success: "green",
9
- warning: "yellow",
10
- info: "cyan",
11
- muted: "gray",
12
- primary: "cyan",
13
- secondary: "blue",
14
- spinner: "green",
15
- progress: "cyan",
16
- label: "white",
17
- dimLabel: "gray",
18
- highlight: "bold"
7
+ background: "#1b212b",
8
+ surface: "#202733",
9
+ surfaceMuted: "#1d2430",
10
+ surfaceElevated: "#252d3b",
11
+ text: "#d8d3c9",
12
+ textMuted: "#918b84",
13
+ textSubtle: "#66625d",
14
+ accent: "#8fc4bf",
15
+ accentActive: "#a7d8d2",
16
+ success: "#8ecb73",
17
+ warning: "#d8ae63",
18
+ danger: "#c97b73",
19
+ border: "#394252",
20
+ borderActive: "#546077",
21
+ error: "#c97b73",
22
+ info: "#8fc4bf",
23
+ muted: "#918b84",
24
+ primary: "#8fc4bf",
25
+ secondary: "#a7d8d2",
26
+ spinner: "#8fc4bf",
27
+ progress: "#8fc4bf",
28
+ label: "#d8d3c9",
29
+ dimLabel: "#66625d",
30
+ highlight: "bold",
31
+ separator: " \xB7 ",
32
+ sectionGap: 1,
33
+ panelPaddingX: 1,
34
+ panelPaddingY: 0
19
35
  };
20
36
 
21
37
  // src/components/Spinner.tsx
@@ -27,7 +43,7 @@ function Spinner({
27
43
  }) {
28
44
  return /* @__PURE__ */ jsxs(Box, { children: [
29
45
  /* @__PURE__ */ jsx(Text, { color, children: /* @__PURE__ */ jsx(InkSpinner, { type }) }),
30
- /* @__PURE__ */ jsxs(Text, { children: [
46
+ /* @__PURE__ */ jsxs(Text, { color: theme.textMuted, children: [
31
47
  " ",
32
48
  label
33
49
  ] })
@@ -38,22 +54,106 @@ function Spinner({
38
54
  import { Box as Box2, Text as Text2 } from "ink";
39
55
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
40
56
  function StatusBar({ left, center, right }) {
41
- return /* @__PURE__ */ jsxs2(Box2, { borderStyle: "single", borderColor: theme.muted, paddingX: 1, children: [
42
- /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, children: /* @__PURE__ */ jsx2(Text2, { color: theme.dimLabel, children: left }) }),
43
- center && /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: theme.info, children: center }) }),
44
- right && /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { color: theme.dimLabel, children: right }) })
45
- ] });
57
+ return /* @__PURE__ */ jsxs2(
58
+ Box2,
59
+ {
60
+ borderStyle: "single",
61
+ borderColor: theme.border,
62
+ backgroundColor: theme.background,
63
+ paddingX: theme.panelPaddingX,
64
+ paddingY: theme.panelPaddingY,
65
+ children: [
66
+ /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, children: /* @__PURE__ */ jsx2(Text2, { color: theme.textMuted, children: left }) }),
67
+ center && /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: theme.accent, children: center }) }),
68
+ right && /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { color: theme.textSubtle, children: right }) })
69
+ ]
70
+ }
71
+ );
46
72
  }
47
73
 
48
74
  // src/components/StreamOutput.tsx
49
75
  import { useMemo } from "react";
50
76
  import { Box as Box3, Text as Text3 } from "ink";
77
+ import chalk from "chalk";
51
78
  import InkSpinner2 from "ink-spinner";
52
79
  import { marked } from "marked";
53
80
  import { markedTerminal } from "marked-terminal";
54
81
  import stripAnsi from "strip-ansi";
55
82
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
56
- marked.use(markedTerminal());
83
+ var bodyText = chalk.hex(theme.text);
84
+ var mutedText = chalk.hex(theme.textMuted);
85
+ var subtleText = chalk.hex(theme.textSubtle);
86
+ var accentText = chalk.hex(theme.accent);
87
+ var accentActiveText = chalk.hex(theme.accentActive);
88
+ var successText = chalk.hex(theme.success);
89
+ var warningText = chalk.hex(theme.warning);
90
+ var dangerText = chalk.hex(theme.danger);
91
+ var markdownHighlightTheme = {
92
+ default: bodyText,
93
+ keyword: accentText,
94
+ built_in: accentActiveText,
95
+ type: accentActiveText,
96
+ literal: accentText,
97
+ number: successText,
98
+ regexp: warningText,
99
+ string: bodyText,
100
+ subst: accentText,
101
+ symbol: bodyText,
102
+ class: accentActiveText,
103
+ function: accentActiveText,
104
+ title: accentActiveText,
105
+ params: mutedText,
106
+ comment: subtleText,
107
+ doctag: subtleText,
108
+ meta: subtleText,
109
+ "meta-keyword": accentText,
110
+ "meta-string": mutedText,
111
+ section: accentText,
112
+ tag: accentText,
113
+ name: accentActiveText,
114
+ "builtin-name": accentActiveText,
115
+ attr: accentText,
116
+ attribute: accentText,
117
+ variable: accentActiveText,
118
+ bullet: mutedText,
119
+ code: successText,
120
+ emphasis: accentText,
121
+ strong: accentActiveText,
122
+ formula: accentText,
123
+ link: accentActiveText,
124
+ quote: mutedText,
125
+ addition: successText,
126
+ deletion: dangerText
127
+ };
128
+ marked.use(
129
+ markedTerminal(
130
+ {
131
+ code: successText,
132
+ blockquote: mutedText,
133
+ html: subtleText,
134
+ heading: accentText,
135
+ firstHeading: accentActiveText.bold,
136
+ hr: subtleText,
137
+ listitem: bodyText,
138
+ list: (body) => bodyText(body),
139
+ table: mutedText,
140
+ paragraph: bodyText,
141
+ strong: accentActiveText.bold,
142
+ em: accentText.italic,
143
+ codespan: successText,
144
+ del: subtleText.strikethrough,
145
+ link: accentActiveText,
146
+ href: mutedText.underline,
147
+ text: bodyText,
148
+ showSectionPrefix: false,
149
+ reflowText: false
150
+ },
151
+ {
152
+ ignoreIllegals: true,
153
+ theme: markdownHighlightTheme
154
+ }
155
+ )
156
+ );
57
157
  function StreamOutput({
58
158
  content,
59
159
  isStreaming,
@@ -79,8 +179,8 @@ function StreamOutput({
79
179
  return lines.slice(start, end).join("\n");
80
180
  }, [rendered, maxVisibleLines, scrollOffset]);
81
181
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
82
- visible && /* @__PURE__ */ jsx3(Text3, { children: visible }),
83
- isStreaming && /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: theme.muted, children: /* @__PURE__ */ jsx3(InkSpinner2, { type: "dots" }) }) })
182
+ visible && /* @__PURE__ */ jsx3(Text3, { color: theme.text, children: visible }),
183
+ isStreaming && /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { color: theme.textMuted, children: /* @__PURE__ */ jsx3(InkSpinner2, { type: "dots" }) }) })
84
184
  ] });
85
185
  }
86
186
 
@@ -90,14 +190,14 @@ import { jsxs as jsxs4 } from "react/jsx-runtime";
90
190
  function ErrorDisplay({ error, context }) {
91
191
  const message = error instanceof Error ? error.message : error;
92
192
  const cause = error instanceof Error && error.cause ? String(error.cause) : void 0;
93
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
94
- /* @__PURE__ */ jsxs4(Text4, { color: theme.error, bold: true, children: [
193
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: theme.sectionGap, children: [
194
+ /* @__PURE__ */ jsxs4(Text4, { color: theme.danger, bold: true, children: [
95
195
  "Error",
96
196
  context ? ` ${context}` : "",
97
197
  ": ",
98
198
  message
99
199
  ] }),
100
- cause && /* @__PURE__ */ jsxs4(Text4, { color: theme.muted, children: [
200
+ cause && /* @__PURE__ */ jsxs4(Text4, { color: theme.textMuted, children: [
101
201
  "Cause: ",
102
202
  cause
103
203
  ] })
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Spinner.tsx","../src/theme.ts","../src/components/StatusBar.tsx","../src/components/StreamOutput.tsx","../src/components/ErrorDisplay.tsx"],"sourcesContent":["import type { ComponentProps } from 'react'\nimport { Box, Text } from 'ink'\nimport InkSpinner from 'ink-spinner'\nimport { theme } from '../theme.js'\n\ntype InkSpinnerType = ComponentProps<typeof InkSpinner>['type']\n\nexport interface SpinnerProps {\n label?: string\n color?: string\n type?: InkSpinnerType\n}\n\nexport function Spinner({\n label = 'Loading...',\n color = theme.spinner,\n type = 'dots',\n}: SpinnerProps) {\n return (\n <Box>\n <Text color={color}>\n <InkSpinner type={type} />\n </Text>\n <Text> {label}</Text>\n </Box>\n )\n}\n","export const theme = {\n error: 'red',\n success: 'green',\n warning: 'yellow',\n info: 'cyan',\n muted: 'gray',\n primary: 'cyan',\n secondary: 'blue',\n spinner: 'green',\n progress: 'cyan',\n label: 'white',\n dimLabel: 'gray',\n highlight: 'bold',\n} as const\n\nexport type Theme = typeof theme\n","import { Box, Text } from 'ink'\nimport { theme } from '../theme.js'\n\nexport interface StatusBarProps {\n left?: string\n center?: string\n right?: string\n}\n\nexport function StatusBar({ left, center, right }: StatusBarProps) {\n return (\n <Box borderStyle=\"single\" borderColor={theme.muted} paddingX={1}>\n <Box flexGrow={1}>\n <Text color={theme.dimLabel}>{left}</Text>\n </Box>\n {center && (\n <Box flexGrow={1} justifyContent=\"center\">\n <Text color={theme.info}>{center}</Text>\n </Box>\n )}\n {right && (\n <Box>\n <Text color={theme.dimLabel}>{right}</Text>\n </Box>\n )}\n </Box>\n )\n}\n","import { useMemo } from 'react'\nimport { Box, Text } from 'ink'\nimport InkSpinner from 'ink-spinner'\nimport { type MarkedExtension, marked } from 'marked'\n// @ts-expect-error marked-terminal has no type declarations\nimport { markedTerminal } from 'marked-terminal'\nimport stripAnsi from 'strip-ansi'\nimport { theme } from '../theme.js'\n\nexport interface StreamOutputProps {\n content: string\n isStreaming: boolean\n enableMarkdown?: boolean\n maxVisibleLines?: number\n /** Lines to scroll up from the bottom (0 = follow latest) */\n scrollOffset?: number\n}\n\n// Configure marked with terminal renderer once at module level\nmarked.use(markedTerminal() as MarkedExtension)\n\nexport function StreamOutput({\n content,\n isStreaming,\n enableMarkdown = true,\n maxVisibleLines,\n scrollOffset = 0,\n}: StreamOutputProps) {\n const rendered = useMemo(() => {\n // Strip incoming ANSI codes, then re-render via marked-terminal if enabled\n const stripped = stripAnsi(content)\n if (!stripped) return ''\n if (enableMarkdown) {\n return (marked.parse(stripped) as string).trimEnd()\n }\n return stripped\n }, [content, enableMarkdown])\n\n // Window to N lines with optional scroll offset from bottom\n const visible = useMemo(() => {\n if (!maxVisibleLines) return rendered\n const lines = rendered.split('\\n')\n const totalLines = lines.length\n const clampedOffset = Math.min(scrollOffset, Math.max(0, totalLines - maxVisibleLines))\n const end = totalLines - clampedOffset\n const start = Math.max(0, end - maxVisibleLines)\n return lines.slice(start, end).join('\\n')\n }, [rendered, maxVisibleLines, scrollOffset])\n\n return (\n <Box flexDirection=\"column\">\n {visible && <Text>{visible}</Text>}\n {isStreaming && (\n <Box>\n <Text color={theme.muted}>\n <InkSpinner type=\"dots\" />\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n","import { Box, Text } from 'ink'\nimport { theme } from '../theme.js'\n\nexport interface ErrorDisplayProps {\n error: Error | string\n context?: string\n}\n\nexport function ErrorDisplay({ error, context }: ErrorDisplayProps) {\n const message = error instanceof Error ? error.message : error\n const cause =\n error instanceof Error && error.cause ? String(error.cause) : undefined\n\n return (\n <Box flexDirection=\"column\">\n <Text color={theme.error} bold>\n Error{context ? ` ${context}` : ''}: {message}\n </Text>\n {cause && <Text color={theme.muted}>Cause: {cause}</Text>}\n </Box>\n )\n}\n"],"mappings":"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","names":["Box","Text","jsx","jsxs","Box","Text","Box","Text","InkSpinner","jsx","jsxs","Box","Text","InkSpinner","Box","Text","jsxs","Box","Text"]}
1
+ {"version":3,"sources":["../src/components/Spinner.tsx","../src/theme.ts","../src/components/StatusBar.tsx","../src/components/StreamOutput.tsx","../src/components/ErrorDisplay.tsx"],"sourcesContent":["import type { ComponentProps } from 'react'\nimport { Box, Text } from 'ink'\nimport InkSpinner from 'ink-spinner'\nimport { theme } from '../theme.js'\n\ntype InkSpinnerType = ComponentProps<typeof InkSpinner>['type']\n\nexport interface SpinnerProps {\n label?: string\n color?: string\n type?: InkSpinnerType\n}\n\nexport function Spinner({\n label = 'Loading...',\n color = theme.spinner,\n type = 'dots',\n}: SpinnerProps) {\n return (\n <Box>\n <Text color={color}>\n <InkSpinner type={type} />\n </Text>\n <Text color={theme.textMuted}> {label}</Text>\n </Box>\n )\n}\n","export const theme = {\n background: '#1b212b',\n surface: '#202733',\n surfaceMuted: '#1d2430',\n surfaceElevated: '#252d3b',\n text: '#d8d3c9',\n textMuted: '#918b84',\n textSubtle: '#66625d',\n accent: '#8fc4bf',\n accentActive: '#a7d8d2',\n success: '#8ecb73',\n warning: '#d8ae63',\n danger: '#c97b73',\n border: '#394252',\n borderActive: '#546077',\n error: '#c97b73',\n info: '#8fc4bf',\n muted: '#918b84',\n primary: '#8fc4bf',\n secondary: '#a7d8d2',\n spinner: '#8fc4bf',\n progress: '#8fc4bf',\n label: '#d8d3c9',\n dimLabel: '#66625d',\n highlight: 'bold',\n separator: ' · ',\n sectionGap: 1,\n panelPaddingX: 1,\n panelPaddingY: 0,\n} as const\n\nexport type Theme = typeof theme\n","import { Box, Text } from 'ink'\nimport { theme } from '../theme.js'\n\nexport interface StatusBarProps {\n left?: string\n center?: string\n right?: string\n}\n\nexport function StatusBar({ left, center, right }: StatusBarProps) {\n return (\n <Box\n borderStyle=\"single\"\n borderColor={theme.border}\n backgroundColor={theme.background}\n paddingX={theme.panelPaddingX}\n paddingY={theme.panelPaddingY}\n >\n <Box flexGrow={1}>\n <Text color={theme.textMuted}>{left}</Text>\n </Box>\n {center && (\n <Box flexGrow={1} justifyContent=\"center\">\n <Text color={theme.accent}>{center}</Text>\n </Box>\n )}\n {right && (\n <Box>\n <Text color={theme.textSubtle}>{right}</Text>\n </Box>\n )}\n </Box>\n )\n}\n","import { useMemo } from 'react'\nimport { Box, Text } from 'ink'\nimport chalk from 'chalk'\nimport InkSpinner from 'ink-spinner'\nimport { type MarkedExtension, marked } from 'marked'\n// @ts-expect-error marked-terminal has no type declarations\nimport { markedTerminal } from 'marked-terminal'\nimport stripAnsi from 'strip-ansi'\nimport { theme } from '../theme.js'\n\nexport interface StreamOutputProps {\n content: string\n isStreaming: boolean\n enableMarkdown?: boolean\n maxVisibleLines?: number\n /** Lines to scroll up from the bottom (0 = follow latest) */\n scrollOffset?: number\n}\n\nconst bodyText = chalk.hex(theme.text)\nconst mutedText = chalk.hex(theme.textMuted)\nconst subtleText = chalk.hex(theme.textSubtle)\nconst accentText = chalk.hex(theme.accent)\nconst accentActiveText = chalk.hex(theme.accentActive)\nconst successText = chalk.hex(theme.success)\nconst warningText = chalk.hex(theme.warning)\nconst dangerText = chalk.hex(theme.danger)\n\nconst markdownHighlightTheme = {\n default: bodyText,\n keyword: accentText,\n built_in: accentActiveText,\n type: accentActiveText,\n literal: accentText,\n number: successText,\n regexp: warningText,\n string: bodyText,\n subst: accentText,\n symbol: bodyText,\n class: accentActiveText,\n function: accentActiveText,\n title: accentActiveText,\n params: mutedText,\n comment: subtleText,\n doctag: subtleText,\n meta: subtleText,\n 'meta-keyword': accentText,\n 'meta-string': mutedText,\n section: accentText,\n tag: accentText,\n name: accentActiveText,\n 'builtin-name': accentActiveText,\n attr: accentText,\n attribute: accentText,\n variable: accentActiveText,\n bullet: mutedText,\n code: successText,\n emphasis: accentText,\n strong: accentActiveText,\n formula: accentText,\n link: accentActiveText,\n quote: mutedText,\n addition: successText,\n deletion: dangerText,\n} as const\n\n// Configure marked with terminal renderer once at module level.\nmarked.use(\n markedTerminal(\n {\n code: successText,\n blockquote: mutedText,\n html: subtleText,\n heading: accentText,\n firstHeading: accentActiveText.bold,\n hr: subtleText,\n listitem: bodyText,\n list: (body: string) => bodyText(body),\n table: mutedText,\n paragraph: bodyText,\n strong: accentActiveText.bold,\n em: accentText.italic,\n codespan: successText,\n del: subtleText.strikethrough,\n link: accentActiveText,\n href: mutedText.underline,\n text: bodyText,\n showSectionPrefix: false,\n reflowText: false,\n },\n {\n ignoreIllegals: true,\n theme: markdownHighlightTheme,\n }\n ) as MarkedExtension\n)\n\nexport function StreamOutput({\n content,\n isStreaming,\n enableMarkdown = true,\n maxVisibleLines,\n scrollOffset = 0,\n}: StreamOutputProps) {\n const rendered = useMemo(() => {\n // Strip incoming ANSI codes, then re-render via marked-terminal if enabled\n const stripped = stripAnsi(content)\n if (!stripped) return ''\n if (enableMarkdown) {\n return (marked.parse(stripped) as string).trimEnd()\n }\n return stripped\n }, [content, enableMarkdown])\n\n // Window to N lines with optional scroll offset from bottom\n const visible = useMemo(() => {\n if (!maxVisibleLines) return rendered\n const lines = rendered.split('\\n')\n const totalLines = lines.length\n const clampedOffset = Math.min(scrollOffset, Math.max(0, totalLines - maxVisibleLines))\n const end = totalLines - clampedOffset\n const start = Math.max(0, end - maxVisibleLines)\n return lines.slice(start, end).join('\\n')\n }, [rendered, maxVisibleLines, scrollOffset])\n\n return (\n <Box flexDirection=\"column\">\n {visible && <Text color={theme.text}>{visible}</Text>}\n {isStreaming && (\n <Box>\n <Text color={theme.textMuted}>\n <InkSpinner type=\"dots\" />\n </Text>\n </Box>\n )}\n </Box>\n )\n}\n","import { Box, Text } from 'ink'\nimport { theme } from '../theme.js'\n\nexport interface ErrorDisplayProps {\n error: Error | string\n context?: string\n}\n\nexport function ErrorDisplay({ error, context }: ErrorDisplayProps) {\n const message = error instanceof Error ? error.message : error\n const cause =\n error instanceof Error && error.cause ? String(error.cause) : undefined\n\n return (\n <Box flexDirection=\"column\" marginTop={theme.sectionGap}>\n <Text color={theme.danger} bold>\n Error{context ? ` ${context}` : ''}: {message}\n </Text>\n {cause && <Text color={theme.textMuted}>Cause: {cause}</Text>}\n </Box>\n )\n}\n"],"mappings":"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","names":["Box","Text","jsx","jsxs","Box","Text","Box","Text","InkSpinner","jsx","jsxs","Box","Text","InkSpinner","Box","Text","jsxs","Box","Text"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtypelabs/ink-components",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Shared Ink (React for CLI) components for the Runtype CLI — spinners, status bars, streaming output, and error displays.",
5
5
  "license": "MIT",
6
6
  "author": "Runtype Labs",
@@ -23,11 +23,11 @@
23
23
  "ink-spinner": "5.0.0",
24
24
  "marked": "^17.0.3",
25
25
  "marked-terminal": "^7.0.0",
26
- "react": "^19.2.0",
26
+ "react": "^19.2.4",
27
27
  "strip-ansi": "^7.2.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/react": "^19.0.0",
30
+ "@types/react": "^19.2.14",
31
31
  "ink-testing-library": "4.0.0",
32
32
  "tsup": "^8.0.2",
33
33
  "typescript": "^5.3.3",