a2uink 0.1.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/.eslintignore +4 -0
- package/.eslintrc.cjs +21 -0
- package/.gitattributes +5 -0
- package/.github/copilot-instructions.md +21 -0
- package/.github/workflows/ci.yml +31 -0
- package/.husky/pre-commit +6 -0
- package/.prettierignore +6 -0
- package/.prettierrc +7 -0
- package/README.md +44 -0
- package/dist/binding.d.ts +3 -0
- package/dist/binding.js +73 -0
- package/dist/catalog.d.ts +6 -0
- package/dist/catalog.js +165 -0
- package/dist/examples/demo.d.ts +1 -0
- package/dist/examples/demo.js +309 -0
- package/dist/focus.d.ts +15 -0
- package/dist/focus.js +68 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/renderer.d.ts +6 -0
- package/dist/renderer.js +144 -0
- package/dist/src/binding.d.ts +8 -0
- package/dist/src/binding.js +141 -0
- package/dist/src/catalog.d.ts +2 -0
- package/dist/src/catalog.js +1 -0
- package/dist/src/components/Box.d.ts +6 -0
- package/dist/src/components/Box.js +23 -0
- package/dist/src/components/Button.d.ts +7 -0
- package/dist/src/components/Button.js +71 -0
- package/dist/src/components/Chart.d.ts +5 -0
- package/dist/src/components/Chart.js +65 -0
- package/dist/src/components/Checkbox.d.ts +7 -0
- package/dist/src/components/Checkbox.js +51 -0
- package/dist/src/components/DateTimeInput.d.ts +1 -0
- package/dist/src/components/DateTimeInput.js +1 -0
- package/dist/src/components/Divider.d.ts +5 -0
- package/dist/src/components/Divider.js +7 -0
- package/dist/src/components/Image.d.ts +5 -0
- package/dist/src/components/Image.js +8 -0
- package/dist/src/components/Input.d.ts +7 -0
- package/dist/src/components/Input.js +124 -0
- package/dist/src/components/List.d.ts +5 -0
- package/dist/src/components/List.js +9 -0
- package/dist/src/components/Modal.d.ts +6 -0
- package/dist/src/components/Modal.js +13 -0
- package/dist/src/components/RadioGroup.d.ts +7 -0
- package/dist/src/components/RadioGroup.js +56 -0
- package/dist/src/components/Select.d.ts +7 -0
- package/dist/src/components/Select.js +66 -0
- package/dist/src/components/Slider.d.ts +7 -0
- package/dist/src/components/Slider.js +74 -0
- package/dist/src/components/Spacer.d.ts +1 -0
- package/dist/src/components/Spacer.js +1 -0
- package/dist/src/components/Table.d.ts +5 -0
- package/dist/src/components/Table.js +14 -0
- package/dist/src/components/Tabs.d.ts +7 -0
- package/dist/src/components/Tabs.js +56 -0
- package/dist/src/components/Text.d.ts +5 -0
- package/dist/src/components/Text.js +15 -0
- package/dist/src/components/helpers.d.ts +4 -0
- package/dist/src/components/helpers.js +39 -0
- package/dist/src/components/index.d.ts +16 -0
- package/dist/src/components/index.js +15 -0
- package/dist/src/components/renderNode.d.ts +4 -0
- package/dist/src/components/renderNode.js +61 -0
- package/dist/src/components/types.d.ts +7 -0
- package/dist/src/components/types.js +1 -0
- package/dist/src/components.d.ts +1 -0
- package/dist/src/components.js +1 -0
- package/dist/src/focus.d.ts +15 -0
- package/dist/src/focus.js +68 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +1 -0
- package/dist/src/renderer.d.ts +6 -0
- package/dist/src/renderer.js +673 -0
- package/dist/src/tree.d.ts +2 -0
- package/dist/src/tree.js +47 -0
- package/dist/src/types.d.ts +92 -0
- package/dist/src/types.js +1 -0
- package/dist/tree.d.ts +2 -0
- package/dist/tree.js +45 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.js +1 -0
- package/docs/demo/README.md +90 -0
- package/docs/demo/app.js +268 -0
- package/docs/demo/index.html +14 -0
- package/docs/demo/package-lock.json +4512 -0
- package/docs/demo/package.json +32 -0
- package/docs/demo/src/App.tsx +1403 -0
- package/docs/demo/src/main.tsx +9 -0
- package/docs/demo/src/setEnv.ts +29 -0
- package/docs/demo/src/shims/fs.js +16 -0
- package/docs/demo/src/shims/process.js +10 -0
- package/docs/demo/src/styles.css +720 -0
- package/docs/demo/styles.css +1 -0
- package/docs/demo/tsconfig.json +14 -0
- package/docs/demo/vite-plugin-node-polyfills/shims/buffer +2 -0
- package/docs/demo/vite-plugin-node-polyfills/shims/global +2 -0
- package/docs/demo/vite-plugin-node-polyfills/shims/process +10 -0
- package/docs/demo/vite.config.js +200 -0
- package/docs/overview.md +277 -0
- package/examples/demo.d.ts +1 -0
- package/examples/demo.js +66 -0
- package/examples/demo.ts +315 -0
- package/package.json +48 -0
- package/src/binding.ts +191 -0
- package/src/catalog.ts +2 -0
- package/src/components/Box.ts +39 -0
- package/src/components/Button.ts +84 -0
- package/src/components/Checkbox.ts +66 -0
- package/src/components/DateTimeInput.ts +1 -0
- package/src/components/Divider.ts +8 -0
- package/src/components/Image.ts +15 -0
- package/src/components/Input.ts +148 -0
- package/src/components/List.ts +15 -0
- package/src/components/Modal.ts +21 -0
- package/src/components/RadioGroup.ts +77 -0
- package/src/components/Select.ts +94 -0
- package/src/components/Slider.ts +98 -0
- package/src/components/Spacer.ts +1 -0
- package/src/components/Table.ts +22 -0
- package/src/components/Tabs.ts +82 -0
- package/src/components/Text.ts +21 -0
- package/src/components/helpers.ts +42 -0
- package/src/components/index.ts +16 -0
- package/src/components/renderNode.ts +73 -0
- package/src/components/types.ts +8 -0
- package/src/components.ts +1 -0
- package/src/focus.ts +94 -0
- package/src/index.ts +12 -0
- package/src/renderer.ts +779 -0
- package/src/tree.ts +63 -0
- package/src/types.ts +110 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { useFocusRegistry } from "../focus.js";
|
|
4
|
+
import { getItemLabel } from "./helpers.js";
|
|
5
|
+
export const A2uiRadioGroup = ({ node, options }) => {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
const focus = useFocusRegistry();
|
|
8
|
+
const { register, unregister } = focus;
|
|
9
|
+
const optionsList = ((_a = node.props.options) !== null && _a !== void 0 ? _a : []);
|
|
10
|
+
const action = node.props.onChange;
|
|
11
|
+
const selectedProp = ((_b = node.props.selectedIndex) !== null && _b !== void 0 ? _b : 0);
|
|
12
|
+
const [selectedIndex, setSelectedIndex] = useState(selectedProp);
|
|
13
|
+
const selectedRef = useRef(selectedIndex);
|
|
14
|
+
const nodeRef = useRef(node);
|
|
15
|
+
const dispatchRef = useRef(options.dispatchAction);
|
|
16
|
+
const handlerRef = useRef(() => undefined);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (selectedRef.current !== selectedProp) {
|
|
19
|
+
setSelectedIndex(selectedProp);
|
|
20
|
+
}
|
|
21
|
+
}, [selectedProp]);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
selectedRef.current = selectedIndex;
|
|
24
|
+
}, [selectedIndex]);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
nodeRef.current = node;
|
|
27
|
+
}, [node]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
dispatchRef.current = options.dispatchAction;
|
|
30
|
+
}, [options.dispatchAction]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
handlerRef.current = (input, key) => {
|
|
33
|
+
const currentIndex = selectedRef.current;
|
|
34
|
+
if (key.upArrow) {
|
|
35
|
+
setSelectedIndex((current) => (current <= 0 ? optionsList.length - 1 : current - 1));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (key.downArrow) {
|
|
39
|
+
setSelectedIndex((current) => (current + 1) % optionsList.length);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if ((key.return || input === " ") && action) {
|
|
43
|
+
const value = optionsList[currentIndex];
|
|
44
|
+
dispatchRef.current(action, nodeRef.current, value);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}, [action, optionsList]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const handler = (input, key) => handlerRef.current(input, key);
|
|
50
|
+
register(node.instanceKey, handler);
|
|
51
|
+
return () => unregister(node.instanceKey);
|
|
52
|
+
}, [register, unregister, node.instanceKey]);
|
|
53
|
+
const isFocused = focus.isFocused(node.instanceKey);
|
|
54
|
+
const labels = useMemo(() => optionsList.map(getItemLabel), [optionsList]);
|
|
55
|
+
return React.createElement(Box, { flexDirection: "column" }, labels.map((label, index) => React.createElement(Text, { key: `${node.instanceKey}_${index}`, inverse: isFocused && index === selectedIndex }, `${isFocused && index === selectedIndex ? "▶ " : " "}${index === selectedIndex ? "(o)" : "( )"} ${label}`)));
|
|
56
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { useFocusRegistry } from "../focus.js";
|
|
4
|
+
import { getItemLabel } from "./helpers.js";
|
|
5
|
+
export const A2uiSelect = ({ node, options }) => {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
const focus = useFocusRegistry();
|
|
8
|
+
const { register, unregister } = focus;
|
|
9
|
+
const items = ((_a = node.props.items) !== null && _a !== void 0 ? _a : []);
|
|
10
|
+
const action = node.props.onSelect;
|
|
11
|
+
const initialIndex = ((_b = node.props.selectedIndex) !== null && _b !== void 0 ? _b : 0);
|
|
12
|
+
const [index, setIndex] = useState(initialIndex);
|
|
13
|
+
const nodeRef = useRef(node);
|
|
14
|
+
const dispatchRef = useRef(options.dispatchAction);
|
|
15
|
+
const indexRef = useRef(index);
|
|
16
|
+
const itemsRef = useRef(items);
|
|
17
|
+
const handlerRef = useRef(() => undefined);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (indexRef.current !== initialIndex) {
|
|
20
|
+
setIndex(initialIndex);
|
|
21
|
+
}
|
|
22
|
+
}, [initialIndex]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
nodeRef.current = node;
|
|
25
|
+
}, [node]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
dispatchRef.current = options.dispatchAction;
|
|
28
|
+
}, [options.dispatchAction]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
indexRef.current = index;
|
|
31
|
+
}, [index]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
itemsRef.current = items;
|
|
34
|
+
}, [items]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
handlerRef.current = (input, key) => {
|
|
37
|
+
const currentItems = itemsRef.current;
|
|
38
|
+
const currentIndex = indexRef.current;
|
|
39
|
+
if (key.upArrow) {
|
|
40
|
+
setIndex((current) => (current <= 0 ? currentItems.length - 1 : current - 1));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (key.downArrow) {
|
|
44
|
+
setIndex((current) => (current + 1) % currentItems.length);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (key.return && action) {
|
|
48
|
+
const value = currentItems[currentIndex];
|
|
49
|
+
dispatchRef.current(action, nodeRef.current, value);
|
|
50
|
+
}
|
|
51
|
+
if (input === " " && action) {
|
|
52
|
+
const value = currentItems[currentIndex];
|
|
53
|
+
dispatchRef.current(action, nodeRef.current, value);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}, [action]);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const handler = (input, key) => handlerRef.current(input, key);
|
|
59
|
+
register(node.instanceKey, handler);
|
|
60
|
+
return () => unregister(node.instanceKey);
|
|
61
|
+
}, [register, unregister, node.instanceKey]);
|
|
62
|
+
const isFocused = focus.isFocused(node.instanceKey);
|
|
63
|
+
const labels = useMemo(() => items.map(getItemLabel), [items]);
|
|
64
|
+
const label = node.props.label;
|
|
65
|
+
return React.createElement(Box, { flexDirection: "column" }, label ? React.createElement(Text, { dimColor: true }, label) : null, React.createElement(Box, { flexDirection: "column" }, labels.map((itemLabel, itemIndex) => React.createElement(Text, { key: `${node.instanceKey}_${itemIndex}`, inverse: isFocused && itemIndex === index }, `${isFocused && itemIndex === index ? "▶ " : " "}${itemLabel}`))));
|
|
66
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { useFocusRegistry } from "../focus.js";
|
|
4
|
+
const BAR_WIDTH = 20;
|
|
5
|
+
export const A2uiSlider = ({ node, options }) => {
|
|
6
|
+
const focus = useFocusRegistry();
|
|
7
|
+
const { register, unregister } = focus;
|
|
8
|
+
const min = coerceNumber(node.props.min, 0);
|
|
9
|
+
const max = coerceNumber(node.props.max, 100);
|
|
10
|
+
const step = coerceNumber(node.props.step, 1);
|
|
11
|
+
const action = node.props.onChange;
|
|
12
|
+
const initial = coerceNumber(node.props.value, min);
|
|
13
|
+
const [value, setValue] = useState(initial);
|
|
14
|
+
const valueRef = useRef(value);
|
|
15
|
+
const nodeRef = useRef(node);
|
|
16
|
+
const dispatchRef = useRef(options.dispatchAction);
|
|
17
|
+
const handlerRef = useRef(() => undefined);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (valueRef.current !== initial) {
|
|
20
|
+
setValue(clamp(initial, min, max));
|
|
21
|
+
}
|
|
22
|
+
}, [initial, min, max]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
valueRef.current = value;
|
|
25
|
+
}, [value]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
nodeRef.current = node;
|
|
28
|
+
}, [node]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
dispatchRef.current = options.dispatchAction;
|
|
31
|
+
}, [options.dispatchAction]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
handlerRef.current = (_input, key) => {
|
|
34
|
+
if (!action) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (key.leftArrow) {
|
|
38
|
+
const next = clamp(valueRef.current - step, min, max);
|
|
39
|
+
setValue(next);
|
|
40
|
+
dispatchRef.current(action, nodeRef.current, next);
|
|
41
|
+
}
|
|
42
|
+
if (key.rightArrow) {
|
|
43
|
+
const next = clamp(valueRef.current + step, min, max);
|
|
44
|
+
setValue(next);
|
|
45
|
+
dispatchRef.current(action, nodeRef.current, next);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}, [action, min, max, step]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const handler = (input, key) => handlerRef.current(input, key);
|
|
51
|
+
register(node.instanceKey, handler);
|
|
52
|
+
return () => unregister(node.instanceKey);
|
|
53
|
+
}, [register, unregister, node.instanceKey]);
|
|
54
|
+
const isFocused = focus.isFocused(node.instanceKey);
|
|
55
|
+
const label = node.props.label;
|
|
56
|
+
const percentage = max === min ? 1 : (value - min) / (max - min);
|
|
57
|
+
const filled = Math.max(0, Math.min(BAR_WIDTH, Math.round(percentage * BAR_WIDTH)));
|
|
58
|
+
const bar = useMemo(() => {
|
|
59
|
+
const fill = "█".repeat(filled);
|
|
60
|
+
const empty = "░".repeat(BAR_WIDTH - filled);
|
|
61
|
+
return `${fill}${empty}`;
|
|
62
|
+
}, [filled]);
|
|
63
|
+
return React.createElement(Box, { flexDirection: "column" }, label ? React.createElement(Text, { dimColor: true }, label) : null, React.createElement(Box, null, React.createElement(Text, { color: isFocused ? "cyan" : undefined }, `${bar} ${value}`)));
|
|
64
|
+
};
|
|
65
|
+
function clamp(value, min, max) {
|
|
66
|
+
return Math.max(min, Math.min(max, value));
|
|
67
|
+
}
|
|
68
|
+
function coerceNumber(value, fallback) {
|
|
69
|
+
const number = typeof value === "number" ? value : Number(value);
|
|
70
|
+
if (Number.isFinite(number)) {
|
|
71
|
+
return number;
|
|
72
|
+
}
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Spacer as A2uiSpacer } from "ink";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Spacer as A2uiSpacer } from "ink";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { formatRow, getColumnWidths, normalizeRow, getItemLabel } from "./helpers.js";
|
|
4
|
+
export const A2uiTable = ({ node }) => {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const columns = ((_a = node.props.columns) !== null && _a !== void 0 ? _a : []);
|
|
7
|
+
const rows = ((_b = node.props.rows) !== null && _b !== void 0 ? _b : []);
|
|
8
|
+
const columnLabels = columns.map(getItemLabel);
|
|
9
|
+
const normalizedRows = rows.map((row) => normalizeRow(row, columns.length));
|
|
10
|
+
const widths = getColumnWidths(columnLabels, normalizedRows);
|
|
11
|
+
const header = formatRow(columnLabels, widths);
|
|
12
|
+
const body = normalizedRows.map((row) => formatRow(row, widths));
|
|
13
|
+
return React.createElement(Box, { flexDirection: "column" }, React.createElement(Text, { bold: true }, header), ...body.map((line, index) => React.createElement(Text, { key: `${node.instanceKey}_row_${index}` }, line)));
|
|
14
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { useFocusRegistry } from "../focus.js";
|
|
4
|
+
import { getItemLabel } from "./helpers.js";
|
|
5
|
+
export const A2uiTabs = ({ node, options }) => {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
const focus = useFocusRegistry();
|
|
8
|
+
const { register, unregister } = focus;
|
|
9
|
+
const tabs = ((_a = node.props.tabs) !== null && _a !== void 0 ? _a : []);
|
|
10
|
+
const action = node.props.onChange;
|
|
11
|
+
const selectedProp = ((_b = node.props.selectedIndex) !== null && _b !== void 0 ? _b : 0);
|
|
12
|
+
const [selectedIndex, setSelectedIndex] = useState(selectedProp);
|
|
13
|
+
const selectedRef = useRef(selectedIndex);
|
|
14
|
+
const nodeRef = useRef(node);
|
|
15
|
+
const dispatchRef = useRef(options.dispatchAction);
|
|
16
|
+
const handlerRef = useRef(() => undefined);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (selectedRef.current !== selectedProp) {
|
|
19
|
+
setSelectedIndex(selectedProp);
|
|
20
|
+
}
|
|
21
|
+
}, [selectedProp]);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
selectedRef.current = selectedIndex;
|
|
24
|
+
}, [selectedIndex]);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
nodeRef.current = node;
|
|
27
|
+
}, [node]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
dispatchRef.current = options.dispatchAction;
|
|
30
|
+
}, [options.dispatchAction]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
handlerRef.current = (input, key) => {
|
|
33
|
+
if (key.leftArrow) {
|
|
34
|
+
setSelectedIndex((current) => (current <= 0 ? tabs.length - 1 : current - 1));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (key.rightArrow) {
|
|
38
|
+
setSelectedIndex((current) => (current + 1) % tabs.length);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if ((key.return || input === " ") && action) {
|
|
42
|
+
const value = tabs[selectedRef.current];
|
|
43
|
+
dispatchRef.current(action, nodeRef.current, value);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}, [action, tabs]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const handler = (input, key) => handlerRef.current(input, key);
|
|
49
|
+
register(node.instanceKey, handler);
|
|
50
|
+
return () => unregister(node.instanceKey);
|
|
51
|
+
}, [register, unregister, node.instanceKey]);
|
|
52
|
+
const isFocused = focus.isFocused(node.instanceKey);
|
|
53
|
+
const labels = useMemo(() => tabs.map(getItemLabel), [tabs]);
|
|
54
|
+
const activeChild = node.children[selectedIndex];
|
|
55
|
+
return React.createElement(Box, { flexDirection: "column" }, React.createElement(Box, { flexDirection: "row" }, labels.map((label, index) => React.createElement(Text, { key: `${node.instanceKey}_tab_${index}`, inverse: isFocused && index === selectedIndex }, ` ${label} `))), activeChild ? options.renderNode(activeChild) : null);
|
|
56
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
export const A2uiText = ({ node }) => {
|
|
4
|
+
var _a, _b, _c;
|
|
5
|
+
const props = node.props;
|
|
6
|
+
const text = ((_b = (_a = props.text) !== null && _a !== void 0 ? _a : props.value) !== null && _b !== void 0 ? _b : "");
|
|
7
|
+
return React.createElement(Text, {
|
|
8
|
+
color: props.color,
|
|
9
|
+
backgroundColor: props.backgroundColor,
|
|
10
|
+
bold: (_c = props.bold) !== null && _c !== void 0 ? _c : true,
|
|
11
|
+
dimColor: props.dim,
|
|
12
|
+
italic: props.italic,
|
|
13
|
+
underline: props.underline
|
|
14
|
+
}, text);
|
|
15
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function getItemLabel(item: unknown): string;
|
|
2
|
+
export declare function normalizeRow(row: unknown, expectedLength: number): string[];
|
|
3
|
+
export declare function getColumnWidths(headers: string[], rows: string[][]): number[];
|
|
4
|
+
export declare function formatRow(cells: string[], widths: number[]): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function getItemLabel(item) {
|
|
2
|
+
var _a, _b;
|
|
3
|
+
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
|
|
4
|
+
return String(item);
|
|
5
|
+
}
|
|
6
|
+
if (item && typeof item === "object") {
|
|
7
|
+
const record = item;
|
|
8
|
+
const label = (_b = (_a = record.label) !== null && _a !== void 0 ? _a : record.text) !== null && _b !== void 0 ? _b : record.name;
|
|
9
|
+
if (label) {
|
|
10
|
+
return String(label);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return JSON.stringify(item);
|
|
14
|
+
}
|
|
15
|
+
export function normalizeRow(row, expectedLength) {
|
|
16
|
+
if (Array.isArray(row)) {
|
|
17
|
+
return row.map(getItemLabel).slice(0, expectedLength);
|
|
18
|
+
}
|
|
19
|
+
if (row && typeof row === "object") {
|
|
20
|
+
const record = row;
|
|
21
|
+
return Object.values(record).map(getItemLabel).slice(0, expectedLength);
|
|
22
|
+
}
|
|
23
|
+
return [getItemLabel(row)];
|
|
24
|
+
}
|
|
25
|
+
export function getColumnWidths(headers, rows) {
|
|
26
|
+
const widths = headers.map((header) => header.length);
|
|
27
|
+
rows.forEach((row) => {
|
|
28
|
+
row.forEach((cell, index) => {
|
|
29
|
+
var _a;
|
|
30
|
+
widths[index] = Math.max((_a = widths[index]) !== null && _a !== void 0 ? _a : 0, cell.length);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
return widths;
|
|
34
|
+
}
|
|
35
|
+
export function formatRow(cells, widths) {
|
|
36
|
+
return cells
|
|
37
|
+
.map((cell, index) => { var _a; return cell.padEnd((_a = widths[index]) !== null && _a !== void 0 ? _a : cell.length, " "); })
|
|
38
|
+
.join(" ");
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type { CatalogRenderOptions } from "./types.js";
|
|
2
|
+
export { renderNode } from "./renderNode.js";
|
|
3
|
+
export { A2uiText } from "./Text.js";
|
|
4
|
+
export { A2uiBox } from "./Box.js";
|
|
5
|
+
export { A2uiButton } from "./Button.js";
|
|
6
|
+
export { A2uiInput } from "./Input.js";
|
|
7
|
+
export { A2uiSelect } from "./Select.js";
|
|
8
|
+
export { A2uiCheckbox } from "./Checkbox.js";
|
|
9
|
+
export { A2uiRadioGroup } from "./RadioGroup.js";
|
|
10
|
+
export { A2uiList } from "./List.js";
|
|
11
|
+
export { A2uiTabs } from "./Tabs.js";
|
|
12
|
+
export { A2uiTable } from "./Table.js";
|
|
13
|
+
export { A2uiSlider } from "./Slider.js";
|
|
14
|
+
export { A2uiModal } from "./Modal.js";
|
|
15
|
+
export { A2uiImage } from "./Image.js";
|
|
16
|
+
export { A2uiDivider } from "./Divider.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { renderNode } from "./renderNode.js";
|
|
2
|
+
export { A2uiText } from "./Text.js";
|
|
3
|
+
export { A2uiBox } from "./Box.js";
|
|
4
|
+
export { A2uiButton } from "./Button.js";
|
|
5
|
+
export { A2uiInput } from "./Input.js";
|
|
6
|
+
export { A2uiSelect } from "./Select.js";
|
|
7
|
+
export { A2uiCheckbox } from "./Checkbox.js";
|
|
8
|
+
export { A2uiRadioGroup } from "./RadioGroup.js";
|
|
9
|
+
export { A2uiList } from "./List.js";
|
|
10
|
+
export { A2uiTabs } from "./Tabs.js";
|
|
11
|
+
export { A2uiTable } from "./Table.js";
|
|
12
|
+
export { A2uiSlider } from "./Slider.js";
|
|
13
|
+
export { A2uiModal } from "./Modal.js";
|
|
14
|
+
export { A2uiImage } from "./Image.js";
|
|
15
|
+
export { A2uiDivider } from "./Divider.js";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "ink";
|
|
3
|
+
import { A2uiText } from "./Text.js";
|
|
4
|
+
import { A2uiBox } from "./Box.js";
|
|
5
|
+
import { A2uiButton } from "./Button.js";
|
|
6
|
+
import { A2uiInput } from "./Input.js";
|
|
7
|
+
import { A2uiSelect } from "./Select.js";
|
|
8
|
+
import { A2uiCheckbox } from "./Checkbox.js";
|
|
9
|
+
import { A2uiRadioGroup } from "./RadioGroup.js";
|
|
10
|
+
import { A2uiList } from "./List.js";
|
|
11
|
+
import { A2uiTabs } from "./Tabs.js";
|
|
12
|
+
import { A2uiTable } from "./Table.js";
|
|
13
|
+
import { A2uiSlider } from "./Slider.js";
|
|
14
|
+
import { A2uiModal } from "./Modal.js";
|
|
15
|
+
import { A2uiImage } from "./Image.js";
|
|
16
|
+
import { A2uiDivider } from "./Divider.js";
|
|
17
|
+
import { Spacer } from "ink";
|
|
18
|
+
export function renderNode(node, options) {
|
|
19
|
+
const optionsWithRender = {
|
|
20
|
+
...options,
|
|
21
|
+
renderNode: (child) => renderNode(child, options)
|
|
22
|
+
};
|
|
23
|
+
switch (node.type) {
|
|
24
|
+
case "Text":
|
|
25
|
+
return React.createElement(A2uiText, { key: node.instanceKey, node });
|
|
26
|
+
case "Box":
|
|
27
|
+
return React.createElement(A2uiBox, { key: node.instanceKey, node }, node.children.map((child) => renderNode(child, options)));
|
|
28
|
+
case "Spacer":
|
|
29
|
+
return React.createElement(Spacer, { key: node.instanceKey });
|
|
30
|
+
case "Input":
|
|
31
|
+
return React.createElement(A2uiInput, { key: node.instanceKey, node, options: optionsWithRender });
|
|
32
|
+
case "TextField":
|
|
33
|
+
return React.createElement(A2uiInput, { key: node.instanceKey, node, options: optionsWithRender });
|
|
34
|
+
case "Button":
|
|
35
|
+
return React.createElement(A2uiButton, { key: node.instanceKey, node, options: optionsWithRender });
|
|
36
|
+
case "Select":
|
|
37
|
+
return React.createElement(A2uiSelect, { key: node.instanceKey, node, options: optionsWithRender });
|
|
38
|
+
case "MultipleChoice":
|
|
39
|
+
return React.createElement(A2uiSelect, { key: node.instanceKey, node, options: optionsWithRender });
|
|
40
|
+
case "Checkbox":
|
|
41
|
+
return React.createElement(A2uiCheckbox, { key: node.instanceKey, node, options: optionsWithRender });
|
|
42
|
+
case "RadioGroup":
|
|
43
|
+
return React.createElement(A2uiRadioGroup, { key: node.instanceKey, node, options: optionsWithRender });
|
|
44
|
+
case "List":
|
|
45
|
+
return React.createElement(A2uiList, { key: node.instanceKey, node });
|
|
46
|
+
case "Tabs":
|
|
47
|
+
return React.createElement(A2uiTabs, { key: node.instanceKey, node, options: optionsWithRender });
|
|
48
|
+
case "Table":
|
|
49
|
+
return React.createElement(A2uiTable, { key: node.instanceKey, node });
|
|
50
|
+
case "Divider":
|
|
51
|
+
return React.createElement(A2uiDivider, { key: node.instanceKey, node });
|
|
52
|
+
case "Slider":
|
|
53
|
+
return React.createElement(A2uiSlider, { key: node.instanceKey, node, options: optionsWithRender });
|
|
54
|
+
case "Modal":
|
|
55
|
+
return React.createElement(A2uiModal, { key: node.instanceKey, node }, node.children.map((child) => renderNode(child, options)));
|
|
56
|
+
case "Image":
|
|
57
|
+
return React.createElement(A2uiImage, { key: node.instanceKey, node });
|
|
58
|
+
default:
|
|
59
|
+
return React.createElement(Text, { key: node.instanceKey, dimColor: true }, `Unsupported: ${node.type}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import type { ActionDef, ResolvedNode } from "../types.js";
|
|
3
|
+
export interface CatalogRenderOptions {
|
|
4
|
+
dispatchAction: (action: ActionDef, node: ResolvedNode, value?: unknown) => void;
|
|
5
|
+
updateLocalDataModel?: (path: string, value: unknown, node: ResolvedNode) => void;
|
|
6
|
+
renderNode: (node: ResolvedNode) => ReactElement;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./components/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./components/index.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Key } from "ink";
|
|
3
|
+
export type FocusKeyHandler = (input: string, key: Key) => void;
|
|
4
|
+
export interface FocusRegistry {
|
|
5
|
+
register(id: string, handler: FocusKeyHandler): void;
|
|
6
|
+
unregister(id: string): void;
|
|
7
|
+
isFocused(id: string): boolean;
|
|
8
|
+
focusNext(): void;
|
|
9
|
+
focusPrev(): void;
|
|
10
|
+
handleKey(input: string, key: Key): void;
|
|
11
|
+
}
|
|
12
|
+
export declare const FocusProvider: React.FC<{
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function useFocusRegistry(): FocusRegistry;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
|
|
2
|
+
const FocusContext = createContext(null);
|
|
3
|
+
export const FocusProvider = ({ children }) => {
|
|
4
|
+
const [order, setOrder] = useState([]);
|
|
5
|
+
const [activeId, setActiveId] = useState(null);
|
|
6
|
+
const handlers = useRef(new Map());
|
|
7
|
+
const register = useCallback((id, handler) => {
|
|
8
|
+
handlers.current.set(id, handler);
|
|
9
|
+
setOrder((current) => (current.includes(id) ? current : [...current, id]));
|
|
10
|
+
setActiveId((current) => current !== null && current !== void 0 ? current : id);
|
|
11
|
+
}, []);
|
|
12
|
+
const unregister = useCallback((id) => {
|
|
13
|
+
handlers.current.delete(id);
|
|
14
|
+
setOrder((current) => current.filter((value) => value !== id));
|
|
15
|
+
setActiveId((current) => (current === id ? null : current));
|
|
16
|
+
}, []);
|
|
17
|
+
const focusNext = useCallback(() => {
|
|
18
|
+
setActiveId((current) => {
|
|
19
|
+
var _a;
|
|
20
|
+
if (!order.length) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (!current) {
|
|
24
|
+
return order[0];
|
|
25
|
+
}
|
|
26
|
+
const index = order.indexOf(current);
|
|
27
|
+
return (_a = order[(index + 1) % order.length]) !== null && _a !== void 0 ? _a : order[0];
|
|
28
|
+
});
|
|
29
|
+
}, [order]);
|
|
30
|
+
const focusPrev = useCallback(() => {
|
|
31
|
+
setActiveId((current) => {
|
|
32
|
+
var _a;
|
|
33
|
+
if (!order.length) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (!current) {
|
|
37
|
+
return order[order.length - 1];
|
|
38
|
+
}
|
|
39
|
+
const index = order.indexOf(current);
|
|
40
|
+
return (_a = order[(index - 1 + order.length) % order.length]) !== null && _a !== void 0 ? _a : order[0];
|
|
41
|
+
});
|
|
42
|
+
}, [order]);
|
|
43
|
+
const handleKey = useCallback((input, key) => {
|
|
44
|
+
if (!activeId) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const handler = handlers.current.get(activeId);
|
|
48
|
+
if (handler) {
|
|
49
|
+
handler(input, key);
|
|
50
|
+
}
|
|
51
|
+
}, [activeId]);
|
|
52
|
+
const registry = useMemo(() => ({
|
|
53
|
+
register,
|
|
54
|
+
unregister,
|
|
55
|
+
isFocused: (id) => activeId === id,
|
|
56
|
+
focusNext,
|
|
57
|
+
focusPrev,
|
|
58
|
+
handleKey
|
|
59
|
+
}), [register, unregister, activeId, focusNext, focusPrev, handleKey]);
|
|
60
|
+
return React.createElement(FocusContext.Provider, { value: registry }, children);
|
|
61
|
+
};
|
|
62
|
+
export function useFocusRegistry() {
|
|
63
|
+
const context = useContext(FocusContext);
|
|
64
|
+
if (!context) {
|
|
65
|
+
throw new Error("FocusRegistry is not available");
|
|
66
|
+
}
|
|
67
|
+
return context;
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createA2uiInkRenderer } from "./renderer.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { A2uiServerMessage, RendererOptions } from "./types.js";
|
|
2
|
+
export interface A2uiInkRenderer {
|
|
3
|
+
handleMessage(message: A2uiServerMessage): void;
|
|
4
|
+
dispose(): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function createA2uiInkRenderer(options?: RendererOptions): A2uiInkRenderer;
|