@taui-standard/ink-adapter 1.0.4 → 1.0.5

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
@@ -4,6 +4,8 @@ The official [Ink](https://github.com/vadimdemedes/ink) adapter for **Terminal A
4
4
 
5
5
  **Author: Tariq Shams**
6
6
 
7
+ ![TAUI HTOP Dashboard](../taui-standards/examples/screenshots/htop_dashboard.png)
8
+
7
9
  ## Features
8
10
  - **Spec-Compliant**: Renders all primitives from TAUI-0001.
9
11
  - **Interactive**: Focus-aware buttons, inputs, and select lists.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { render, Box, Text, useFocusManager } from 'ink';
3
+ import { render, Box, Text, useFocusManager, useInput } from 'ink';
4
4
  import { TAUIRenderer } from './renderer.js';
5
5
  export * from './renderer.js';
6
6
  export const TAUIApp = ({ runtime }) => {
@@ -12,18 +12,14 @@ export const TAUIApp = ({ runtime }) => {
12
12
  });
13
13
  }, [runtime]);
14
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
- }, []);
15
+ useInput((input, key) => {
16
+ if (key.tab) {
17
+ if (key.shift)
18
+ focusPrevious();
19
+ else
20
+ focusNext();
21
+ }
22
+ });
27
23
  if (!doc)
28
24
  return _jsx(Text, { children: "Loading..." });
29
25
  return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(TAUIRenderer, { node: doc.screen, runtime: runtime }) }));
package/dist/renderer.js CHANGED
@@ -4,7 +4,7 @@ import TextInput from 'ink-text-input';
4
4
  export const TAUIText = ({ node }) => {
5
5
  if (node.type !== 'Text')
6
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 }));
7
+ return (_jsx(Text, { color: node.style?.color, backgroundColor: node.style?.backgroundColor === 'black' ? 'black' : node.style?.backgroundColor, bold: node.style?.bold, dimColor: node.style?.dim, italic: node.style?.italic, underline: node.style?.underline, children: node.content }));
8
8
  };
9
9
  export const TAUIBox = ({ node, runtime }) => {
10
10
  if (!['Box', 'Row', 'Column', 'Grid', 'Panel'].includes(node.type))
@@ -26,7 +26,7 @@ export const TAUIBox = ({ node, runtime }) => {
26
26
  export const TAUIButton = ({ node, runtime }) => {
27
27
  const { isFocused } = useFocus({ id: node.id });
28
28
  useInput((input, key) => {
29
- if (isFocused && (key.return || input === ' ')) {
29
+ if (isFocused && (key.return || input === '\r' || input === ' ')) {
30
30
  runtime.dispatchRawEvent({
31
31
  type: 'action',
32
32
  targetId: node.id,
@@ -1,8 +1,121 @@
1
1
  import { TAUIRuntime } from '@taui-standard/core';
2
2
  import { startTAUI } from '@taui-standard/ink-adapter';
3
- import htopDoc from '../../../taui-standards/examples/htop.json' assert { type: 'json' };
3
+ import os from 'os';
4
4
 
5
5
  const runtime = new TAUIRuntime();
6
- runtime.setDocument(htopDoc);
6
+
7
+ function getHtopData() {
8
+ const totalMem = os.totalmem();
9
+ const freeMem = os.freemem();
10
+ const usedMem = totalMem - freeMem;
11
+ const loadAvg = os.loadavg();
12
+ const cpus = os.cpus();
13
+
14
+ // Mock process list since node:os doesn't provide it easily without spawning child processes
15
+ const mockProcesses = [
16
+ ["1234", "root", loadAvg[0].toFixed(1), "1.2", "systemd"],
17
+ ["5678", "user", (loadAvg[1] / 2).toFixed(1), "0.5", "bash"],
18
+ ["9012", "user", (loadAvg[2] * 4).toFixed(1), "8.1", "agent-process"],
19
+ ["4321", "root", "0.0", "0.1", "kworker"],
20
+ ["1111", "user", "1.5", "2.3", "node"],
21
+ ];
22
+
23
+ const doc: any = {
24
+ version: "1.0",
25
+ metadata: {
26
+ title: "TAUI Real-time Dashboard",
27
+ author: "Tariq Shams"
28
+ },
29
+ screen: {
30
+ type: "Grid",
31
+ style: { backgroundColor: "black", color: "#FF8C00" },
32
+ rows: ["12", "1fr", "3"],
33
+ columns: ["1fr", "1fr"],
34
+ children: [
35
+ {
36
+ id: "cpu_panel",
37
+ type: "Panel",
38
+ title: "CPU Load",
39
+ borderStyle: "rounded",
40
+ style: { color: "#FF8C00" },
41
+ children: [
42
+ {
43
+ type: "Progress",
44
+ value: Math.min(loadAvg[0] / cpus.length, 1),
45
+ label: `Load 1m: ${(loadAvg[0]).toFixed(2)}`,
46
+ style: { color: "#FF8C00" }
47
+ },
48
+ {
49
+ type: "Text",
50
+ content: `Arch: ${os.arch()} | CPUs: ${cpus.length}`,
51
+ style: { color: "#FF8C00" }
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ id: "mem_panel",
57
+ type: "Panel",
58
+ title: "Memory",
59
+ borderStyle: "rounded",
60
+ style: { color: "#FF8C00" },
61
+ children: [
62
+ {
63
+ type: "Progress",
64
+ value: usedMem / totalMem,
65
+ label: `MEM ${(usedMem / 1024 / 1024 / 1024).toFixed(1)}G / ${(totalMem / 1024 / 1024 / 1024).toFixed(1)}G`,
66
+ style: { color: "#FF8C00" }
67
+ },
68
+ {
69
+ type: "Text",
70
+ content: `Free: ${(freeMem / 1024 / 1024 / 1024).toFixed(1)}G`,
71
+ style: { color: "#FF8C00" }
72
+ }
73
+ ]
74
+ },
75
+ {
76
+ id: "process_table",
77
+ type: "Table",
78
+ headers: ["PID", "USER", "CPU%", "MEM%", "COMMAND"],
79
+ rows: mockProcesses,
80
+ style: { color: "#FF8C00" }
81
+ },
82
+ {
83
+ id: "footer",
84
+ type: "Row",
85
+ children: [
86
+ { type: "Text", content: " [q: Quit] [Tab: Navigate] [Enter: Action] ", style: { color: "#FF8C00", bold: true } },
87
+ { id: "refresh_btn", type: "Button", label: "Refresh Now" },
88
+ { id: "quit_kb", type: "KeyBinding", key: "q", action: "quit" }
89
+ ]
90
+ }
91
+ ]
92
+ }
93
+ };
94
+ return doc;
95
+ }
96
+
97
+ // Initial set
98
+ runtime.setDocument(getHtopData());
99
+
100
+ // Update loop (every 2 seconds)
101
+ const interval = setInterval(() => {
102
+ try {
103
+ runtime.setDocument(getHtopData());
104
+ } catch (e) {
105
+ // Suppress errors during shutdown
106
+ }
107
+ }, 2000);
108
+
109
+ // Handle interaction
110
+ runtime.onEvent((event) => {
111
+ if (event.type === 'key' && (event.payload?.key === 'q' || event.targetId === 'quit_kb')) {
112
+ clearInterval(interval);
113
+ process.exit(0);
114
+ }
115
+
116
+ if (event.type === 'action' && event.targetId === 'refresh_btn') {
117
+ runtime.setDocument(getHtopData());
118
+ }
119
+ });
7
120
 
8
121
  startTAUI(runtime);
@@ -9,10 +9,14 @@
9
9
  "version": "1.0.0",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
- "@taui-standard/core": "1.0.3",
13
- "@taui-standard/ink-adapter": "1.0.3",
14
- "@taui-standard/validator": "1.0.3",
12
+ "@taui-standard/core": "1.0.4",
13
+ "@taui-standard/ink-adapter": "1.0.4",
14
+ "@taui-standard/validator": "1.0.4",
15
15
  "tsx": "^4.7.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^20.11.0",
19
+ "@types/react": "^18.2.0"
16
20
  }
17
21
  },
18
22
  "node_modules/@alcalzone/ansi-tokenize": {
@@ -445,31 +449,31 @@
445
449
  }
446
450
  },
447
451
  "node_modules/@taui-standard/core": {
448
- "version": "1.0.3",
449
- "resolved": "https://registry.npmjs.org/@taui-standard/core/-/core-1.0.3.tgz",
450
- "integrity": "sha512-Dq9uSYDLad+ao4r2XwNuJtwLLqrvxd4rjKCOgZIVSDS56yWaxOfflMYbgxpVTf3+O34sFk9zIyUhk+fRrahHsQ==",
452
+ "version": "1.0.4",
453
+ "resolved": "https://registry.npmjs.org/@taui-standard/core/-/core-1.0.4.tgz",
454
+ "integrity": "sha512-0hmBgQAbIsDNytkgQaGI7FNBdERnDT+4jpMZOqyJAjK0IGMbRYyFLWLwoTT1VqdSltiNTgfqUzmN+2mFMmZcmA==",
451
455
  "license": "Apache-2.0",
452
456
  "dependencies": {
453
- "@taui-standard/validator": "^1.0.3"
457
+ "@taui-standard/validator": "^1.0.4"
454
458
  }
455
459
  },
456
460
  "node_modules/@taui-standard/ink-adapter": {
457
- "version": "1.0.3",
458
- "resolved": "https://registry.npmjs.org/@taui-standard/ink-adapter/-/ink-adapter-1.0.3.tgz",
459
- "integrity": "sha512-gpo8USYLMTS6y2Wj0caVutFl2Fl8NzD9h3byrd1mfJ1zMOdYA5XcIk9Md46kX6lyxEorP9jzO18bGMEig2zD5A==",
461
+ "version": "1.0.4",
462
+ "resolved": "https://registry.npmjs.org/@taui-standard/ink-adapter/-/ink-adapter-1.0.4.tgz",
463
+ "integrity": "sha512-Ti6965n02TmOxfmRIEdK8t3aevVke+7xBgzmzbLjkmBay7lF3UZJT7P+vPmXPEAEVVdIiKd+QoyeC70Yj9CQkQ==",
460
464
  "license": "Apache-2.0",
461
465
  "dependencies": {
462
- "@taui-standard/core": "^1.0.3",
463
- "@taui-standard/validator": "^1.0.3",
466
+ "@taui-standard/core": "^1.0.4",
467
+ "@taui-standard/validator": "^1.0.4",
464
468
  "ink": "^5.0.0",
465
469
  "ink-text-input": "^6.0.0",
466
470
  "react": "^18.2.0"
467
471
  }
468
472
  },
469
473
  "node_modules/@taui-standard/validator": {
470
- "version": "1.0.3",
471
- "resolved": "https://registry.npmjs.org/@taui-standard/validator/-/validator-1.0.3.tgz",
472
- "integrity": "sha512-pEBSioeWCWknQOOEJjLhmuIqqq984Nzw4tdU2GI9+UlLKmr9vGrATxqY8jDpZDuTZjseIkhBs9KclcXW6s8J4Q==",
474
+ "version": "1.0.4",
475
+ "resolved": "https://registry.npmjs.org/@taui-standard/validator/-/validator-1.0.4.tgz",
476
+ "integrity": "sha512-EigZ9lXcINLUz61G6fHGuG5SKcbNr90CJc5fLsRW6Kamenl5NKQLoWga0m0rLLoiRguUDBWMoOqPA33G7CUHvg==",
473
477
  "license": "Apache-2.0",
474
478
  "dependencies": {
475
479
  "ajv": "^8.12.0",
@@ -477,6 +481,34 @@
477
481
  "zod": "^3.22.4"
478
482
  }
479
483
  },
484
+ "node_modules/@types/node": {
485
+ "version": "20.19.30",
486
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
487
+ "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
488
+ "dev": true,
489
+ "license": "MIT",
490
+ "dependencies": {
491
+ "undici-types": "~6.21.0"
492
+ }
493
+ },
494
+ "node_modules/@types/prop-types": {
495
+ "version": "15.7.15",
496
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
497
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
498
+ "devOptional": true,
499
+ "license": "MIT"
500
+ },
501
+ "node_modules/@types/react": {
502
+ "version": "18.3.27",
503
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
504
+ "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
505
+ "devOptional": true,
506
+ "license": "MIT",
507
+ "dependencies": {
508
+ "@types/prop-types": "*",
509
+ "csstype": "^3.2.2"
510
+ }
511
+ },
480
512
  "node_modules/ajv": {
481
513
  "version": "8.17.1",
482
514
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
@@ -653,6 +685,13 @@
653
685
  "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
654
686
  }
655
687
  },
688
+ "node_modules/csstype": {
689
+ "version": "3.2.3",
690
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
691
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
692
+ "devOptional": true,
693
+ "license": "MIT"
694
+ },
656
695
  "node_modules/emoji-regex": {
657
696
  "version": "10.6.0",
658
697
  "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
@@ -1135,6 +1174,13 @@
1135
1174
  "url": "https://github.com/sponsors/sindresorhus"
1136
1175
  }
1137
1176
  },
1177
+ "node_modules/undici-types": {
1178
+ "version": "6.21.0",
1179
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
1180
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
1181
+ "dev": true,
1182
+ "license": "MIT"
1183
+ },
1138
1184
  "node_modules/widest-line": {
1139
1185
  "version": "5.0.0",
1140
1186
  "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
@@ -13,5 +13,9 @@
13
13
  "@taui-standard/ink-adapter": "1.0.4",
14
14
  "@taui-standard/validator": "1.0.4",
15
15
  "tsx": "^4.7.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^20.11.0",
19
+ "@types/react": "^18.2.0"
16
20
  }
17
21
  }
@@ -7,10 +7,11 @@
7
7
  "": {
8
8
  "name": "example-dashboard",
9
9
  "version": "1.0.0",
10
+ "license": "Apache-2.0",
10
11
  "dependencies": {
11
- "@taui-standard/core": "0.0.1",
12
- "@taui-standard/ink-adapter": "0.0.1",
13
- "@taui-standard/validator": "0.0.1",
12
+ "@taui-standard/core": "1.0.4",
13
+ "@taui-standard/ink-adapter": "1.0.4",
14
+ "@taui-standard/validator": "1.0.4",
14
15
  "tsx": "^4.7.0"
15
16
  }
16
17
  },
@@ -444,31 +445,31 @@
444
445
  }
445
446
  },
446
447
  "node_modules/@taui-standard/core": {
447
- "version": "0.0.1",
448
- "resolved": "https://registry.npmjs.org/@taui-standard/core/-/core-0.0.1.tgz",
449
- "integrity": "sha512-LpS9OZYpsYXm5ooGE4oYhUfmVjqkDzc+zGQVivExbvrgz3JE+xhs9PnX1BQy4XqMaLn4GwtIXy7nX6A42+A2uA==",
448
+ "version": "1.0.4",
449
+ "resolved": "https://registry.npmjs.org/@taui-standard/core/-/core-1.0.4.tgz",
450
+ "integrity": "sha512-0hmBgQAbIsDNytkgQaGI7FNBdERnDT+4jpMZOqyJAjK0IGMbRYyFLWLwoTT1VqdSltiNTgfqUzmN+2mFMmZcmA==",
450
451
  "license": "Apache-2.0",
451
452
  "dependencies": {
452
- "@taui-standard/validator": "^0.0.1"
453
+ "@taui-standard/validator": "^1.0.4"
453
454
  }
454
455
  },
455
456
  "node_modules/@taui-standard/ink-adapter": {
456
- "version": "0.0.1",
457
- "resolved": "https://registry.npmjs.org/@taui-standard/ink-adapter/-/ink-adapter-0.0.1.tgz",
458
- "integrity": "sha512-i51EnEi0p5j6NcqLz2AxbcQoCHJj/q2tkIx/I2pnDneCpy2lL4bbw/hgPmet0+9TNoIpkw4ZnEPUWG839uC+Pw==",
457
+ "version": "1.0.4",
458
+ "resolved": "https://registry.npmjs.org/@taui-standard/ink-adapter/-/ink-adapter-1.0.4.tgz",
459
+ "integrity": "sha512-Ti6965n02TmOxfmRIEdK8t3aevVke+7xBgzmzbLjkmBay7lF3UZJT7P+vPmXPEAEVVdIiKd+QoyeC70Yj9CQkQ==",
459
460
  "license": "Apache-2.0",
460
461
  "dependencies": {
461
- "@taui-standard/core": "^0.0.1",
462
- "@taui-standard/validator": "^0.0.1",
462
+ "@taui-standard/core": "^1.0.4",
463
+ "@taui-standard/validator": "^1.0.4",
463
464
  "ink": "^5.0.0",
464
465
  "ink-text-input": "^6.0.0",
465
466
  "react": "^18.2.0"
466
467
  }
467
468
  },
468
469
  "node_modules/@taui-standard/validator": {
469
- "version": "0.0.1",
470
- "resolved": "https://registry.npmjs.org/@taui-standard/validator/-/validator-0.0.1.tgz",
471
- "integrity": "sha512-CyiTbKmcL1qgTQIkqSZ/zursgFbFDwHXdOPCZkSYpcZew+WsClh1bTBc1bp+Uo+mwBQO3TLwWzYuTE0n+I91iQ==",
470
+ "version": "1.0.4",
471
+ "resolved": "https://registry.npmjs.org/@taui-standard/validator/-/validator-1.0.4.tgz",
472
+ "integrity": "sha512-EigZ9lXcINLUz61G6fHGuG5SKcbNr90CJc5fLsRW6Kamenl5NKQLoWga0m0rLLoiRguUDBWMoOqPA33G7CUHvg==",
472
473
  "license": "Apache-2.0",
473
474
  "dependencies": {
474
475
  "ajv": "^8.12.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taui-standard/ink-adapter",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "description": "Ink adapter for Terminal Agent UI (TAUI)",
6
6
  "main": "dist/index.js",
package/src/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, Box, Text, useFocusManager } from 'ink';
2
+ import { render, Box, Text, useFocusManager, useInput } from 'ink';
3
3
  import { TAUIRuntime } from '@taui-standard/core';
4
4
  import { TAUIRenderer } from './renderer.js';
5
5
 
@@ -20,16 +20,12 @@ export const TAUIApp: React.FC<TAUIAppProps> = ({ runtime }) => {
20
20
  }, [runtime]);
21
21
 
22
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
- }, []);
23
+ useInput((input, key) => {
24
+ if (key.tab) {
25
+ if (key.shift) focusPrevious();
26
+ else focusNext();
27
+ }
28
+ });
33
29
 
34
30
  if (!doc) return <Text>Loading...</Text>;
35
31
 
package/src/renderer.tsx CHANGED
@@ -14,7 +14,7 @@ export const TAUIText: React.FC<RendererProps> = ({ node }) => {
14
14
  return (
15
15
  <Text
16
16
  color={node.style?.color}
17
- backgroundColor={node.style?.backgroundColor}
17
+ backgroundColor={node.style?.backgroundColor === 'black' ? 'black' : node.style?.backgroundColor}
18
18
  bold={node.style?.bold}
19
19
  dimColor={node.style?.dim}
20
20
  italic={node.style?.italic}
@@ -64,7 +64,7 @@ export const TAUIButton: React.FC<RendererProps> = ({ node, runtime }) => {
64
64
  const { isFocused } = useFocus({ id: node.id });
65
65
 
66
66
  useInput((input: string, key: any) => {
67
- if (isFocused && (key.return || input === ' ')) {
67
+ if (isFocused && (key.return || input === '\r' || input === ' ')) {
68
68
  runtime.dispatchRawEvent({
69
69
  type: 'action',
70
70
  targetId: node.id,