@taui-standard/ink-adapter 0.0.1

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.
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { TAUIRuntime } from '@taui-standard/core';
3
+ export * from './renderer.js';
4
+ export interface TAUIAppProps {
5
+ runtime: TAUIRuntime;
6
+ }
7
+ export declare const TAUIApp: React.FC<TAUIAppProps>;
8
+ export declare function startTAUI(runtime: TAUIRuntime): import("ink").Instance;
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { render, Box, Text, useFocusManager } from 'ink';
4
+ import { TAUIRenderer } from './renderer.js';
5
+ export * from './renderer.js';
6
+ export const TAUIApp = ({ runtime }) => {
7
+ const [doc, setDoc] = React.useState(runtime.getDocument());
8
+ const { focusNext, focusPrevious } = useFocusManager();
9
+ React.useEffect(() => {
10
+ return runtime.onEvent(() => {
11
+ setDoc({ ...runtime.getDocument() });
12
+ });
13
+ }, [runtime]);
14
+ // Handle global tab navigation
15
+ React.useEffect(() => {
16
+ const handleKey = (input, key) => {
17
+ if (key.tab) {
18
+ if (key.shift)
19
+ focusPrevious();
20
+ else
21
+ focusNext();
22
+ }
23
+ };
24
+ // Note: useInput is inside components, but we could add a global listener here if needed.
25
+ // However, Ink's focus manager handles Tab by default if components are wrapped in <Box focusable>.
26
+ }, []);
27
+ if (!doc)
28
+ return _jsx(Text, { children: "Loading..." });
29
+ return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(TAUIRenderer, { node: doc.screen, runtime: runtime }) }));
30
+ };
31
+ export function startTAUI(runtime) {
32
+ return render(_jsx(TAUIApp, { runtime: runtime }));
33
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { TAUINode } from '@taui-standard/validator';
3
+ import { TAUIRuntime } from '@taui-standard/core';
4
+ export interface RendererProps {
5
+ node: TAUINode;
6
+ runtime: TAUIRuntime;
7
+ }
8
+ export declare const TAUIText: React.FC<RendererProps>;
9
+ export declare const TAUIBox: React.FC<RendererProps>;
10
+ export declare const TAUIButton: React.FC<RendererProps>;
11
+ export declare const TAUIInput: React.FC<RendererProps>;
12
+ export declare const TAUIProgress: React.FC<RendererProps>;
13
+ export declare const TAUIKeyBinding: React.FC<RendererProps>;
14
+ export declare const TAUIRenderer: React.FC<RendererProps>;
@@ -0,0 +1,97 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useInput, useFocus } from 'ink';
3
+ import TextInput from 'ink-text-input';
4
+ export const TAUIText = ({ node }) => {
5
+ if (node.type !== 'Text')
6
+ return null;
7
+ return (_jsx(Text, { color: node.style?.color, backgroundColor: node.style?.backgroundColor, bold: node.style?.bold, dimColor: node.style?.dim, italic: node.style?.italic, underline: node.style?.underline, children: node.content }));
8
+ };
9
+ export const TAUIBox = ({ node, runtime }) => {
10
+ if (!['Box', 'Row', 'Column', 'Grid', 'Panel'].includes(node.type))
11
+ return null;
12
+ const flexDirection = node.type === 'Row' ? 'row' : 'column';
13
+ const borderStyle = node.type === 'Panel' ? (node.borderStyle || 'single') : undefined;
14
+ return (_jsxs(Box, { flexDirection: flexDirection, padding: typeof node.padding === 'number' ? node.padding : undefined, gap: node.gap, borderStyle: borderStyle === 'none' ? undefined : borderStyle, children: [node.type === 'Panel' && node.title && (_jsx(Box, { position: "absolute", marginTop: -1, marginLeft: 1, paddingX: 1, children: _jsx(Text, { bold: true, children: node.title }) })), node.children?.map((child, idx) => (_jsx(TAUIRenderer, { node: child, runtime: runtime }, child.id || `${node.type}-${idx}`)))] }));
15
+ };
16
+ export const TAUIButton = ({ node, runtime }) => {
17
+ const { isFocused } = useFocus({ id: node.id });
18
+ useInput((input, key) => {
19
+ if (isFocused && (key.return || input === ' ')) {
20
+ runtime.dispatchRawEvent({
21
+ type: 'action',
22
+ targetId: node.id,
23
+ timestamp: new Date().toISOString()
24
+ });
25
+ }
26
+ });
27
+ return (_jsx(Box, { borderStyle: "round", borderColor: isFocused ? 'cyan' : 'gray', paddingX: 1, children: _jsx(Text, { color: isFocused ? 'cyan' : undefined, children: node.label }) }));
28
+ };
29
+ export const TAUIInput = ({ node, runtime }) => {
30
+ const { isFocused } = useFocus({ id: node.id });
31
+ const onChange = (value) => {
32
+ runtime.dispatchRawEvent({
33
+ type: 'change',
34
+ targetId: node.id,
35
+ payload: { value },
36
+ timestamp: new Date().toISOString()
37
+ });
38
+ };
39
+ return (_jsxs(Box, { flexDirection: "row", gap: 1, children: [node.placeholder && _jsxs(Text, { dimColor: true, children: [node.placeholder, ": "] }), _jsx(Box, { borderStyle: "single", borderColor: isFocused ? 'yellow' : 'gray', paddingX: 1, minWidth: 10, children: isFocused ? (_jsx(TextInput, { value: node.value || '', onChange: onChange, mask: node.mask ? '*' : undefined })) : (_jsx(Text, { children: node.mask ? '*'.repeat((node.value || '').length) : (node.value || ' ') })) })] }));
40
+ };
41
+ export const TAUIProgress = ({ node }) => {
42
+ const width = 20;
43
+ const val = node.value || 0;
44
+ const completed = Math.round(val * width);
45
+ const bar = '\u2588'.repeat(completed) + '\u2591'.repeat(width - completed);
46
+ return (_jsxs(Box, { flexDirection: "row", gap: 1, children: [node.label && _jsx(Text, { children: node.label }), _jsx(Text, { color: "cyan", children: bar }), _jsxs(Text, { children: [Math.round(val * 100), "%"] })] }));
47
+ };
48
+ export const TAUIKeyBinding = ({ node, runtime }) => {
49
+ if (node.type !== 'KeyBinding')
50
+ return null;
51
+ useInput((input, key) => {
52
+ const matchesKey = input === node.key ||
53
+ (key.return && node.key === 'enter') ||
54
+ (key.backspace && node.key === 'backspace') ||
55
+ (key.escape && node.key === 'escape') ||
56
+ (key.tab && node.key === 'tab') ||
57
+ (key.upArrow && node.key === 'up') ||
58
+ (key.downArrow && node.key === 'down') ||
59
+ (key.leftArrow && node.key === 'left') ||
60
+ (key.rightArrow && node.key === 'right');
61
+ const matchesModifiers = (!!node.ctrl === key.ctrl) &&
62
+ (!!node.meta === key.meta) &&
63
+ (!!node.shift === key.shift);
64
+ if (matchesKey && matchesModifiers) {
65
+ runtime.dispatchRawEvent({
66
+ type: 'key',
67
+ targetId: node.id || 'global',
68
+ payload: { key: node.key, ctrl: node.ctrl, meta: node.meta, shift: node.shift },
69
+ timestamp: new Date().toISOString()
70
+ });
71
+ }
72
+ });
73
+ return null;
74
+ };
75
+ export const TAUIRenderer = (props) => {
76
+ const { node } = props;
77
+ switch (node.type) {
78
+ case 'Box':
79
+ case 'Row':
80
+ case 'Column':
81
+ case 'Grid':
82
+ case 'Panel':
83
+ return _jsx(TAUIBox, { ...props });
84
+ case 'Text':
85
+ return _jsx(TAUIText, { ...props });
86
+ case 'Button':
87
+ return _jsx(TAUIButton, { ...props });
88
+ case 'Input':
89
+ return _jsx(TAUIInput, { ...props });
90
+ case 'Progress':
91
+ return _jsx(TAUIProgress, { ...props });
92
+ case 'KeyBinding':
93
+ return _jsx(TAUIKeyBinding, { ...props });
94
+ default:
95
+ return _jsxs(Text, { dimColor: true, children: ["Unsupported node type: ", node.type] });
96
+ }
97
+ };
@@ -0,0 +1,8 @@
1
+ import { TAUIRuntime } from '@taui-standard/core';
2
+ import { startTAUI } from '@taui-standard/ink-adapter';
3
+ import htopDoc from '../../taui-standards/examples/htop.json';
4
+
5
+ const runtime = new TAUIRuntime();
6
+ runtime.setDocument(htopDoc);
7
+
8
+ startTAUI(runtime);
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "example-htop",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "tsx index.ts"
8
+ },
9
+ "dependencies": {
10
+ "@taui-standard/core": "0.0.1",
11
+ "@taui-standard/ink-adapter": "0.0.1",
12
+ "@taui-standard/validator": "0.0.1",
13
+ "tsx": "^4.7.0"
14
+ }
15
+ }
@@ -0,0 +1,89 @@
1
+ import { TAUIRuntime } from '@taui-standard/core';
2
+ import { startTAUI } from '@taui-standard/ink-adapter';
3
+
4
+ const runtime = new TAUIRuntime();
5
+
6
+ // Initial state
7
+ let userEmail = '';
8
+ let selectedPriority = 'medium';
9
+ let logs: string[] = ['System initialized. Press "q" to exit.'];
10
+
11
+ function updateUI() {
12
+ runtime.setDocument({
13
+ version: "1.0",
14
+ metadata: { title: "TAUI Advanced Dashboard" },
15
+ screen: {
16
+ type: "Panel",
17
+ title: "TAUI interactive Demo",
18
+ padding: 1,
19
+ children: [
20
+ {
21
+ type: "KeyBinding",
22
+ id: "exit-key",
23
+ key: "q",
24
+ action: "exit"
25
+ },
26
+ {
27
+ type: "Column",
28
+ gap: 1,
29
+ children: [
30
+ {
31
+ type: "Input",
32
+ id: "email-input",
33
+ placeholder: "User Email",
34
+ value: userEmail
35
+ },
36
+ {
37
+ type: "Select",
38
+ id: "priority-select",
39
+ value: selectedPriority,
40
+ options: [
41
+ { label: "Low Priority", value: "low" },
42
+ { label: "Medium Priority", value: "medium" },
43
+ { label: "High Priority", value: "high" }
44
+ ]
45
+ },
46
+ {
47
+ type: "Row",
48
+ gap: 2,
49
+ children: [
50
+ { type: "Button", id: "submit-btn", label: "Deploy Agent" },
51
+ { type: "Button", id: "clear-btn", label: "Clear Logs" }
52
+ ]
53
+ },
54
+ {
55
+ type: "Panel",
56
+ title: "Activity Logs",
57
+ children: logs.slice(-5).map((msg, i) => ({
58
+ type: "Text",
59
+ id: `log-${i}`,
60
+ content: `> ${msg}`,
61
+ style: { dim: true }
62
+ }))
63
+ }
64
+ ]
65
+ }
66
+ ]
67
+ }
68
+ });
69
+ }
70
+
71
+ runtime.onEvent((event) => {
72
+ if (event.type === 'change' && event.targetId === 'email-input') {
73
+ userEmail = (event.payload as any).value;
74
+ logs.push(`Email changed: ${userEmail}`);
75
+ } else if (event.type === 'change' && event.targetId === 'priority-select') {
76
+ selectedPriority = (event.payload as any).value;
77
+ logs.push(`Priority set to: ${selectedPriority}`);
78
+ } else if (event.type === 'action' && event.targetId === 'submit-btn') {
79
+ logs.push(`DEPLOYING agent to ${userEmail} [Priority: ${selectedPriority}]`);
80
+ } else if (event.type === 'action' && event.targetId === 'clear-btn') {
81
+ logs = ['Logs cleared.'];
82
+ } else if (event.type === 'key' && event.targetId === 'exit-key') {
83
+ process.exit(0);
84
+ }
85
+ updateUI();
86
+ });
87
+
88
+ updateUI();
89
+ startTAUI(runtime);
@@ -0,0 +1,544 @@
1
+ {
2
+ "name": "example-dashboard",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "example-dashboard",
9
+ "version": "1.0.0",
10
+ "dependencies": {
11
+ "@taui-standard/core": "file:../../taui-core",
12
+ "@taui-standard/ink-adapter": "file:..",
13
+ "@taui-standard/validator": "file:../../taui-validator",
14
+ "tsx": "^4.7.0"
15
+ }
16
+ },
17
+ "..": {},
18
+ "../../taui-core": {},
19
+ "../../taui-validator": {},
20
+ "node_modules/@esbuild/aix-ppc64": {
21
+ "version": "0.27.2",
22
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
23
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
24
+ "cpu": [
25
+ "ppc64"
26
+ ],
27
+ "license": "MIT",
28
+ "optional": true,
29
+ "os": [
30
+ "aix"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18"
34
+ }
35
+ },
36
+ "node_modules/@esbuild/android-arm": {
37
+ "version": "0.27.2",
38
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
39
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
40
+ "cpu": [
41
+ "arm"
42
+ ],
43
+ "license": "MIT",
44
+ "optional": true,
45
+ "os": [
46
+ "android"
47
+ ],
48
+ "engines": {
49
+ "node": ">=18"
50
+ }
51
+ },
52
+ "node_modules/@esbuild/android-arm64": {
53
+ "version": "0.27.2",
54
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
55
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
56
+ "cpu": [
57
+ "arm64"
58
+ ],
59
+ "license": "MIT",
60
+ "optional": true,
61
+ "os": [
62
+ "android"
63
+ ],
64
+ "engines": {
65
+ "node": ">=18"
66
+ }
67
+ },
68
+ "node_modules/@esbuild/android-x64": {
69
+ "version": "0.27.2",
70
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
71
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
72
+ "cpu": [
73
+ "x64"
74
+ ],
75
+ "license": "MIT",
76
+ "optional": true,
77
+ "os": [
78
+ "android"
79
+ ],
80
+ "engines": {
81
+ "node": ">=18"
82
+ }
83
+ },
84
+ "node_modules/@esbuild/darwin-arm64": {
85
+ "version": "0.27.2",
86
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
87
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
88
+ "cpu": [
89
+ "arm64"
90
+ ],
91
+ "license": "MIT",
92
+ "optional": true,
93
+ "os": [
94
+ "darwin"
95
+ ],
96
+ "engines": {
97
+ "node": ">=18"
98
+ }
99
+ },
100
+ "node_modules/@esbuild/darwin-x64": {
101
+ "version": "0.27.2",
102
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
103
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
104
+ "cpu": [
105
+ "x64"
106
+ ],
107
+ "license": "MIT",
108
+ "optional": true,
109
+ "os": [
110
+ "darwin"
111
+ ],
112
+ "engines": {
113
+ "node": ">=18"
114
+ }
115
+ },
116
+ "node_modules/@esbuild/freebsd-arm64": {
117
+ "version": "0.27.2",
118
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
119
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
120
+ "cpu": [
121
+ "arm64"
122
+ ],
123
+ "license": "MIT",
124
+ "optional": true,
125
+ "os": [
126
+ "freebsd"
127
+ ],
128
+ "engines": {
129
+ "node": ">=18"
130
+ }
131
+ },
132
+ "node_modules/@esbuild/freebsd-x64": {
133
+ "version": "0.27.2",
134
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
135
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
136
+ "cpu": [
137
+ "x64"
138
+ ],
139
+ "license": "MIT",
140
+ "optional": true,
141
+ "os": [
142
+ "freebsd"
143
+ ],
144
+ "engines": {
145
+ "node": ">=18"
146
+ }
147
+ },
148
+ "node_modules/@esbuild/linux-arm": {
149
+ "version": "0.27.2",
150
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
151
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
152
+ "cpu": [
153
+ "arm"
154
+ ],
155
+ "license": "MIT",
156
+ "optional": true,
157
+ "os": [
158
+ "linux"
159
+ ],
160
+ "engines": {
161
+ "node": ">=18"
162
+ }
163
+ },
164
+ "node_modules/@esbuild/linux-arm64": {
165
+ "version": "0.27.2",
166
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
167
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
168
+ "cpu": [
169
+ "arm64"
170
+ ],
171
+ "license": "MIT",
172
+ "optional": true,
173
+ "os": [
174
+ "linux"
175
+ ],
176
+ "engines": {
177
+ "node": ">=18"
178
+ }
179
+ },
180
+ "node_modules/@esbuild/linux-ia32": {
181
+ "version": "0.27.2",
182
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
183
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
184
+ "cpu": [
185
+ "ia32"
186
+ ],
187
+ "license": "MIT",
188
+ "optional": true,
189
+ "os": [
190
+ "linux"
191
+ ],
192
+ "engines": {
193
+ "node": ">=18"
194
+ }
195
+ },
196
+ "node_modules/@esbuild/linux-loong64": {
197
+ "version": "0.27.2",
198
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
199
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
200
+ "cpu": [
201
+ "loong64"
202
+ ],
203
+ "license": "MIT",
204
+ "optional": true,
205
+ "os": [
206
+ "linux"
207
+ ],
208
+ "engines": {
209
+ "node": ">=18"
210
+ }
211
+ },
212
+ "node_modules/@esbuild/linux-mips64el": {
213
+ "version": "0.27.2",
214
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
215
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
216
+ "cpu": [
217
+ "mips64el"
218
+ ],
219
+ "license": "MIT",
220
+ "optional": true,
221
+ "os": [
222
+ "linux"
223
+ ],
224
+ "engines": {
225
+ "node": ">=18"
226
+ }
227
+ },
228
+ "node_modules/@esbuild/linux-ppc64": {
229
+ "version": "0.27.2",
230
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
231
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
232
+ "cpu": [
233
+ "ppc64"
234
+ ],
235
+ "license": "MIT",
236
+ "optional": true,
237
+ "os": [
238
+ "linux"
239
+ ],
240
+ "engines": {
241
+ "node": ">=18"
242
+ }
243
+ },
244
+ "node_modules/@esbuild/linux-riscv64": {
245
+ "version": "0.27.2",
246
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
247
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
248
+ "cpu": [
249
+ "riscv64"
250
+ ],
251
+ "license": "MIT",
252
+ "optional": true,
253
+ "os": [
254
+ "linux"
255
+ ],
256
+ "engines": {
257
+ "node": ">=18"
258
+ }
259
+ },
260
+ "node_modules/@esbuild/linux-s390x": {
261
+ "version": "0.27.2",
262
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
263
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
264
+ "cpu": [
265
+ "s390x"
266
+ ],
267
+ "license": "MIT",
268
+ "optional": true,
269
+ "os": [
270
+ "linux"
271
+ ],
272
+ "engines": {
273
+ "node": ">=18"
274
+ }
275
+ },
276
+ "node_modules/@esbuild/linux-x64": {
277
+ "version": "0.27.2",
278
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
279
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
280
+ "cpu": [
281
+ "x64"
282
+ ],
283
+ "license": "MIT",
284
+ "optional": true,
285
+ "os": [
286
+ "linux"
287
+ ],
288
+ "engines": {
289
+ "node": ">=18"
290
+ }
291
+ },
292
+ "node_modules/@esbuild/netbsd-arm64": {
293
+ "version": "0.27.2",
294
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
295
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
296
+ "cpu": [
297
+ "arm64"
298
+ ],
299
+ "license": "MIT",
300
+ "optional": true,
301
+ "os": [
302
+ "netbsd"
303
+ ],
304
+ "engines": {
305
+ "node": ">=18"
306
+ }
307
+ },
308
+ "node_modules/@esbuild/netbsd-x64": {
309
+ "version": "0.27.2",
310
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
311
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
312
+ "cpu": [
313
+ "x64"
314
+ ],
315
+ "license": "MIT",
316
+ "optional": true,
317
+ "os": [
318
+ "netbsd"
319
+ ],
320
+ "engines": {
321
+ "node": ">=18"
322
+ }
323
+ },
324
+ "node_modules/@esbuild/openbsd-arm64": {
325
+ "version": "0.27.2",
326
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
327
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
328
+ "cpu": [
329
+ "arm64"
330
+ ],
331
+ "license": "MIT",
332
+ "optional": true,
333
+ "os": [
334
+ "openbsd"
335
+ ],
336
+ "engines": {
337
+ "node": ">=18"
338
+ }
339
+ },
340
+ "node_modules/@esbuild/openbsd-x64": {
341
+ "version": "0.27.2",
342
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
343
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
344
+ "cpu": [
345
+ "x64"
346
+ ],
347
+ "license": "MIT",
348
+ "optional": true,
349
+ "os": [
350
+ "openbsd"
351
+ ],
352
+ "engines": {
353
+ "node": ">=18"
354
+ }
355
+ },
356
+ "node_modules/@esbuild/openharmony-arm64": {
357
+ "version": "0.27.2",
358
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
359
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
360
+ "cpu": [
361
+ "arm64"
362
+ ],
363
+ "license": "MIT",
364
+ "optional": true,
365
+ "os": [
366
+ "openharmony"
367
+ ],
368
+ "engines": {
369
+ "node": ">=18"
370
+ }
371
+ },
372
+ "node_modules/@esbuild/sunos-x64": {
373
+ "version": "0.27.2",
374
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
375
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
376
+ "cpu": [
377
+ "x64"
378
+ ],
379
+ "license": "MIT",
380
+ "optional": true,
381
+ "os": [
382
+ "sunos"
383
+ ],
384
+ "engines": {
385
+ "node": ">=18"
386
+ }
387
+ },
388
+ "node_modules/@esbuild/win32-arm64": {
389
+ "version": "0.27.2",
390
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
391
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
392
+ "cpu": [
393
+ "arm64"
394
+ ],
395
+ "license": "MIT",
396
+ "optional": true,
397
+ "os": [
398
+ "win32"
399
+ ],
400
+ "engines": {
401
+ "node": ">=18"
402
+ }
403
+ },
404
+ "node_modules/@esbuild/win32-ia32": {
405
+ "version": "0.27.2",
406
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
407
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
408
+ "cpu": [
409
+ "ia32"
410
+ ],
411
+ "license": "MIT",
412
+ "optional": true,
413
+ "os": [
414
+ "win32"
415
+ ],
416
+ "engines": {
417
+ "node": ">=18"
418
+ }
419
+ },
420
+ "node_modules/@esbuild/win32-x64": {
421
+ "version": "0.27.2",
422
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
423
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
424
+ "cpu": [
425
+ "x64"
426
+ ],
427
+ "license": "MIT",
428
+ "optional": true,
429
+ "os": [
430
+ "win32"
431
+ ],
432
+ "engines": {
433
+ "node": ">=18"
434
+ }
435
+ },
436
+ "node_modules/@taui-standard/core": {
437
+ "resolved": "../../taui-core",
438
+ "link": true
439
+ },
440
+ "node_modules/@taui-standard/ink-adapter": {
441
+ "resolved": "..",
442
+ "link": true
443
+ },
444
+ "node_modules/@taui-standard/validator": {
445
+ "resolved": "../../taui-validator",
446
+ "link": true
447
+ },
448
+ "node_modules/esbuild": {
449
+ "version": "0.27.2",
450
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
451
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
452
+ "hasInstallScript": true,
453
+ "license": "MIT",
454
+ "bin": {
455
+ "esbuild": "bin/esbuild"
456
+ },
457
+ "engines": {
458
+ "node": ">=18"
459
+ },
460
+ "optionalDependencies": {
461
+ "@esbuild/aix-ppc64": "0.27.2",
462
+ "@esbuild/android-arm": "0.27.2",
463
+ "@esbuild/android-arm64": "0.27.2",
464
+ "@esbuild/android-x64": "0.27.2",
465
+ "@esbuild/darwin-arm64": "0.27.2",
466
+ "@esbuild/darwin-x64": "0.27.2",
467
+ "@esbuild/freebsd-arm64": "0.27.2",
468
+ "@esbuild/freebsd-x64": "0.27.2",
469
+ "@esbuild/linux-arm": "0.27.2",
470
+ "@esbuild/linux-arm64": "0.27.2",
471
+ "@esbuild/linux-ia32": "0.27.2",
472
+ "@esbuild/linux-loong64": "0.27.2",
473
+ "@esbuild/linux-mips64el": "0.27.2",
474
+ "@esbuild/linux-ppc64": "0.27.2",
475
+ "@esbuild/linux-riscv64": "0.27.2",
476
+ "@esbuild/linux-s390x": "0.27.2",
477
+ "@esbuild/linux-x64": "0.27.2",
478
+ "@esbuild/netbsd-arm64": "0.27.2",
479
+ "@esbuild/netbsd-x64": "0.27.2",
480
+ "@esbuild/openbsd-arm64": "0.27.2",
481
+ "@esbuild/openbsd-x64": "0.27.2",
482
+ "@esbuild/openharmony-arm64": "0.27.2",
483
+ "@esbuild/sunos-x64": "0.27.2",
484
+ "@esbuild/win32-arm64": "0.27.2",
485
+ "@esbuild/win32-ia32": "0.27.2",
486
+ "@esbuild/win32-x64": "0.27.2"
487
+ }
488
+ },
489
+ "node_modules/fsevents": {
490
+ "version": "2.3.3",
491
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
492
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
493
+ "hasInstallScript": true,
494
+ "license": "MIT",
495
+ "optional": true,
496
+ "os": [
497
+ "darwin"
498
+ ],
499
+ "engines": {
500
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
501
+ }
502
+ },
503
+ "node_modules/get-tsconfig": {
504
+ "version": "4.13.0",
505
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
506
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
507
+ "license": "MIT",
508
+ "dependencies": {
509
+ "resolve-pkg-maps": "^1.0.0"
510
+ },
511
+ "funding": {
512
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
513
+ }
514
+ },
515
+ "node_modules/resolve-pkg-maps": {
516
+ "version": "1.0.0",
517
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
518
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
519
+ "license": "MIT",
520
+ "funding": {
521
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
522
+ }
523
+ },
524
+ "node_modules/tsx": {
525
+ "version": "4.21.0",
526
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
527
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
528
+ "license": "MIT",
529
+ "dependencies": {
530
+ "esbuild": "~0.27.0",
531
+ "get-tsconfig": "^4.7.5"
532
+ },
533
+ "bin": {
534
+ "tsx": "dist/cli.mjs"
535
+ },
536
+ "engines": {
537
+ "node": ">=18.0.0"
538
+ },
539
+ "optionalDependencies": {
540
+ "fsevents": "~2.3.3"
541
+ }
542
+ }
543
+ }
544
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "example-dashboard",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "tsx index.ts"
8
+ },
9
+ "dependencies": {
10
+ "@taui-standard/core": "0.0.1",
11
+ "@taui-standard/ink-adapter": "0.0.1",
12
+ "@taui-standard/validator": "0.0.1",
13
+ "tsx": "^4.7.0"
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@taui-standard/ink-adapter",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Ink adapter for Terminal Agent UI (TAUI)",
6
+ "main": "dist/index.js",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "vitest run"
13
+ },
14
+ "author": "Tariq Shams",
15
+ "license": "Apache-2.0",
16
+ "dependencies": {
17
+ "@taui-standard/core": "^0.0.1",
18
+ "@taui-standard/validator": "^0.0.1",
19
+ "ink": "^5.0.0",
20
+ "ink-text-input": "^6.0.0",
21
+ "react": "^18.2.0"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.3.3",
25
+ "@types/react": "^18.2.0",
26
+ "vitest": "^1.2.1"
27
+ }
28
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { render, Box, Text, useFocusManager } from 'ink';
3
+ import { TAUIRuntime } from '@taui-standard/core';
4
+ import { TAUIRenderer } from './renderer.js';
5
+
6
+ export * from './renderer.js';
7
+
8
+ export interface TAUIAppProps {
9
+ runtime: TAUIRuntime;
10
+ }
11
+
12
+ export const TAUIApp: React.FC<TAUIAppProps> = ({ runtime }) => {
13
+ const [doc, setDoc] = React.useState(runtime.getDocument());
14
+ const { focusNext, focusPrevious } = useFocusManager();
15
+
16
+ React.useEffect(() => {
17
+ return runtime.onEvent(() => {
18
+ setDoc({ ...runtime.getDocument()! });
19
+ });
20
+ }, [runtime]);
21
+
22
+ // Handle global tab navigation
23
+ React.useEffect(() => {
24
+ const handleKey = (input: string, key: any) => {
25
+ if (key.tab) {
26
+ if (key.shift) focusPrevious();
27
+ else focusNext();
28
+ }
29
+ };
30
+ // Note: useInput is inside components, but we could add a global listener here if needed.
31
+ // However, Ink's focus manager handles Tab by default if components are wrapped in <Box focusable>.
32
+ }, []);
33
+
34
+ if (!doc) return <Text>Loading...</Text>;
35
+
36
+ return (
37
+ <Box flexDirection="column" padding={1}>
38
+ <TAUIRenderer node={doc.screen} runtime={runtime} />
39
+ </Box>
40
+ );
41
+ };
42
+
43
+ export function startTAUI(runtime: TAUIRuntime) {
44
+ return render(<TAUIApp runtime={runtime} />);
45
+ }
@@ -0,0 +1,168 @@
1
+ import React from 'react';
2
+ import { Box, Text, useInput, useFocus } from 'ink';
3
+ import TextInput from 'ink-text-input';
4
+ import { TAUINode } from '@taui-standard/validator';
5
+ import { TAUIRuntime } from '@taui-standard/core';
6
+
7
+ export interface RendererProps {
8
+ node: TAUINode;
9
+ runtime: TAUIRuntime;
10
+ }
11
+
12
+ export const TAUIText: React.FC<RendererProps> = ({ node }) => {
13
+ if (node.type !== 'Text') return null;
14
+ return (
15
+ <Text
16
+ color={node.style?.color}
17
+ backgroundColor={node.style?.backgroundColor}
18
+ bold={node.style?.bold}
19
+ dimColor={node.style?.dim}
20
+ italic={node.style?.italic}
21
+ underline={node.style?.underline}
22
+ >
23
+ {node.content}
24
+ </Text>
25
+ );
26
+ };
27
+
28
+ export const TAUIBox: React.FC<RendererProps> = ({ node, runtime }) => {
29
+ if (!['Box', 'Row', 'Column', 'Grid', 'Panel'].includes(node.type)) return null;
30
+
31
+ const flexDirection = node.type === 'Row' ? 'row' : 'column';
32
+ const borderStyle = node.type === 'Panel' ? (node.borderStyle || 'single') : undefined;
33
+
34
+ return (
35
+ <Box
36
+ flexDirection={flexDirection}
37
+ padding={typeof node.padding === 'number' ? node.padding : undefined}
38
+ gap={node.gap}
39
+ borderStyle={borderStyle === 'none' ? undefined : borderStyle as any}
40
+ >
41
+ {node.type === 'Panel' && node.title && (
42
+ <Box position="absolute" marginTop={-1} marginLeft={1} paddingX={1}>
43
+ <Text bold>{node.title}</Text>
44
+ </Box>
45
+ )}
46
+ {node.children?.map((child: TAUINode, idx: number) => (
47
+ <TAUIRenderer key={child.id || `${node.type}-${idx}`} node={child} runtime={runtime} />
48
+ ))}
49
+ </Box>
50
+ );
51
+ };
52
+
53
+ export const TAUIButton: React.FC<RendererProps> = ({ node, runtime }) => {
54
+ const { isFocused } = useFocus({ id: node.id });
55
+
56
+ useInput((input: string, key: any) => {
57
+ if (isFocused && (key.return || input === ' ')) {
58
+ runtime.dispatchRawEvent({
59
+ type: 'action',
60
+ targetId: node.id,
61
+ timestamp: new Date().toISOString()
62
+ });
63
+ }
64
+ });
65
+
66
+ return (
67
+ <Box borderStyle="round" borderColor={isFocused ? 'cyan' : 'gray'} paddingX={1}>
68
+ <Text color={isFocused ? 'cyan' : undefined}>{node.label}</Text>
69
+ </Box>
70
+ );
71
+ };
72
+
73
+ export const TAUIInput: React.FC<RendererProps> = ({ node, runtime }) => {
74
+ const { isFocused } = useFocus({ id: node.id });
75
+
76
+ const onChange = (value: string) => {
77
+ runtime.dispatchRawEvent({
78
+ type: 'change',
79
+ targetId: node.id,
80
+ payload: { value },
81
+ timestamp: new Date().toISOString()
82
+ });
83
+ };
84
+
85
+ return (
86
+ <Box flexDirection="row" gap={1}>
87
+ {node.placeholder && <Text dimColor>{node.placeholder}: </Text>}
88
+ <Box borderStyle="single" borderColor={isFocused ? 'yellow' : 'gray'} paddingX={1} minWidth={10}>
89
+ {isFocused ? (
90
+ <TextInput value={node.value || ''} onChange={onChange} mask={node.mask ? '*' : undefined} />
91
+ ) : (
92
+ <Text>{node.mask ? '*'.repeat((node.value || '').length) : (node.value || ' ')}</Text>
93
+ )}
94
+ </Box>
95
+ </Box>
96
+ );
97
+ };
98
+
99
+ export const TAUIProgress: React.FC<RendererProps> = ({ node }) => {
100
+ const width = 20;
101
+ const val = (node as any).value || 0;
102
+ const completed = Math.round(val * width);
103
+ const bar = '\u2588'.repeat(completed) + '\u2591'.repeat(width - completed);
104
+
105
+ return (
106
+ <Box flexDirection="row" gap={1}>
107
+ {(node as any).label && <Text>{(node as any).label}</Text>}
108
+ <Text color="cyan">{bar}</Text>
109
+ <Text>{Math.round(val * 100)}%</Text>
110
+ </Box>
111
+ );
112
+ };
113
+
114
+ export const TAUIKeyBinding: React.FC<RendererProps> = ({ node, runtime }) => {
115
+ if (node.type !== 'KeyBinding') return null;
116
+
117
+ useInput((input: string, key: any) => {
118
+ const matchesKey = input === node.key ||
119
+ (key.return && node.key === 'enter') ||
120
+ (key.backspace && node.key === 'backspace') ||
121
+ (key.escape && node.key === 'escape') ||
122
+ (key.tab && node.key === 'tab') ||
123
+ (key.upArrow && node.key === 'up') ||
124
+ (key.downArrow && node.key === 'down') ||
125
+ (key.leftArrow && node.key === 'left') ||
126
+ (key.rightArrow && node.key === 'right');
127
+
128
+ const matchesModifiers =
129
+ (!!node.ctrl === key.ctrl) &&
130
+ (!!node.meta === key.meta) &&
131
+ (!!node.shift === key.shift);
132
+
133
+ if (matchesKey && matchesModifiers) {
134
+ runtime.dispatchRawEvent({
135
+ type: 'key',
136
+ targetId: node.id || 'global',
137
+ payload: { key: node.key, ctrl: node.ctrl, meta: node.meta, shift: node.shift },
138
+ timestamp: new Date().toISOString()
139
+ });
140
+ }
141
+ });
142
+
143
+ return null;
144
+ };
145
+
146
+ export const TAUIRenderer: React.FC<RendererProps> = (props) => {
147
+ const { node } = props;
148
+ switch (node.type) {
149
+ case 'Box':
150
+ case 'Row':
151
+ case 'Column':
152
+ case 'Grid':
153
+ case 'Panel':
154
+ return <TAUIBox {...props} />;
155
+ case 'Text':
156
+ return <TAUIText {...props} />;
157
+ case 'Button':
158
+ return <TAUIButton {...props} />;
159
+ case 'Input':
160
+ return <TAUIInput {...props} />;
161
+ case 'Progress':
162
+ return <TAUIProgress {...props} />;
163
+ case 'KeyBinding':
164
+ return <TAUIKeyBinding {...props} />;
165
+ default:
166
+ return <Text dimColor>Unsupported node type: {node.type}</Text>;
167
+ }
168
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "jsx": "react-jsx",
7
+ "declaration": true,
8
+ "outDir": "./dist",
9
+ "rootDir": "./src",
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "forceConsistentCasingInFileNames": true
14
+ },
15
+ "include": ["src/**/*"]
16
+ }