tinky 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/LICENSE +47 -0
- package/README.ja-JP.md +350 -0
- package/README.md +350 -0
- package/README.zh-CN.md +350 -0
- package/lib/colorize.d.ts +16 -0
- package/lib/colorize.js +55 -0
- package/lib/components/AccessibilityContext.d.ts +9 -0
- package/lib/components/AccessibilityContext.js +10 -0
- package/lib/components/App.d.ts +169 -0
- package/lib/components/App.js +314 -0
- package/lib/components/AppContext.d.ts +14 -0
- package/lib/components/AppContext.js +11 -0
- package/lib/components/BackgroundContext.d.ts +10 -0
- package/lib/components/BackgroundContext.js +5 -0
- package/lib/components/Box.d.ts +153 -0
- package/lib/components/Box.js +57 -0
- package/lib/components/ErrorOverview.d.ts +9 -0
- package/lib/components/ErrorOverview.js +49 -0
- package/lib/components/FocusContext.d.ts +51 -0
- package/lib/components/FocusContext.js +35 -0
- package/lib/components/Newline.d.ts +29 -0
- package/lib/components/Newline.js +21 -0
- package/lib/components/Spacer.d.ts +19 -0
- package/lib/components/Spacer.js +23 -0
- package/lib/components/Static.d.ts +48 -0
- package/lib/components/Static.js +46 -0
- package/lib/components/StderrContext.d.ts +21 -0
- package/lib/components/StderrContext.js +12 -0
- package/lib/components/StdinContext.d.ts +36 -0
- package/lib/components/StdinContext.js +16 -0
- package/lib/components/StdoutContext.d.ts +22 -0
- package/lib/components/StdoutContext.js +13 -0
- package/lib/components/Text.d.ts +84 -0
- package/lib/components/Text.js +74 -0
- package/lib/components/Transform.d.ts +41 -0
- package/lib/components/Transform.js +32 -0
- package/lib/devtools-window-polyfill.d.ts +1 -0
- package/lib/devtools-window-polyfill.js +70 -0
- package/lib/devtools.d.ts +1 -0
- package/lib/devtools.js +8 -0
- package/lib/dom.d.ts +130 -0
- package/lib/dom.js +209 -0
- package/lib/get-max-width.d.ts +9 -0
- package/lib/get-max-width.js +14 -0
- package/lib/hooks/use-app.d.ts +26 -0
- package/lib/hooks/use-app.js +28 -0
- package/lib/hooks/use-focus-manager.d.ts +40 -0
- package/lib/hooks/use-focus-manager.js +18 -0
- package/lib/hooks/use-focus.d.ts +67 -0
- package/lib/hooks/use-focus.js +65 -0
- package/lib/hooks/use-input.d.ts +118 -0
- package/lib/hooks/use-input.js +101 -0
- package/lib/hooks/use-is-screen-reader-enabled.d.ts +7 -0
- package/lib/hooks/use-is-screen-reader-enabled.js +12 -0
- package/lib/hooks/use-stderr.d.ts +6 -0
- package/lib/hooks/use-stderr.js +8 -0
- package/lib/hooks/use-stdin.d.ts +6 -0
- package/lib/hooks/use-stdin.js +8 -0
- package/lib/hooks/use-stdout.d.ts +7 -0
- package/lib/hooks/use-stdout.js +9 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.js +20 -0
- package/lib/instances.d.ts +10 -0
- package/lib/instances.js +9 -0
- package/lib/log-update.d.ts +32 -0
- package/lib/log-update.js +147 -0
- package/lib/measure-element.d.ts +19 -0
- package/lib/measure-element.js +13 -0
- package/lib/measure-text.d.ts +13 -0
- package/lib/measure-text.js +26 -0
- package/lib/output.d.ts +73 -0
- package/lib/output.js +190 -0
- package/lib/parse-keypress.d.ts +20 -0
- package/lib/parse-keypress.js +228 -0
- package/lib/reconciler.d.ts +12 -0
- package/lib/reconciler.js +282 -0
- package/lib/render-background.d.ts +11 -0
- package/lib/render-background.js +32 -0
- package/lib/render-border.d.ts +13 -0
- package/lib/render-border.js +82 -0
- package/lib/render-node-to-output.d.ts +43 -0
- package/lib/render-node-to-output.js +179 -0
- package/lib/render.d.ts +129 -0
- package/lib/render.js +84 -0
- package/lib/renderer.d.ts +27 -0
- package/lib/renderer.js +62 -0
- package/lib/signal-exit.d.ts +11 -0
- package/lib/signal-exit.js +24 -0
- package/lib/squash-text-nodes.d.ts +10 -0
- package/lib/squash-text-nodes.js +36 -0
- package/lib/styles.d.ts +402 -0
- package/lib/styles.js +633 -0
- package/lib/taffy-node.d.ts +39 -0
- package/lib/taffy-node.js +60 -0
- package/lib/tinky.d.ts +153 -0
- package/lib/tinky.js +396 -0
- package/lib/wrap-text.d.ts +11 -0
- package/lib/wrap-text.js +38 -0
- package/package.json +87 -0
package/README.md
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">Tinky</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>React for CLIs, re-imagined with the Taffy layout engine</strong>
|
|
5
|
+
</p>
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="./README.zh-CN.md">中文</a> · <a href="./README.ja-JP.md">日本語</a>
|
|
8
|
+
</p>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
[](https://www.npmjs.com/package/tinky)
|
|
15
|
+
|
|
16
|
+
Tinky is a modern React-based framework for building beautiful and interactive command-line interfaces. It leverages the powerful [Taffy](https://github.com/DioxusLabs/taffy) layout engine to provide **CSS Flexbox and Grid** layout support in the terminal.
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
- 🎨 **React Components** — Build CLIs using familiar React patterns and JSX syntax
|
|
21
|
+
- 📐 **Flexbox & Grid Layout** — Full CSS Flexbox and CSS Grid support powered by Taffy
|
|
22
|
+
- ⌨️ **Keyboard Input** — Built-in hooks for handling keyboard input and focus management
|
|
23
|
+
- 🎯 **Focus Management** — Tab/Shift+Tab navigation with customizable focus behavior
|
|
24
|
+
- 🖼️ **Borders & Backgrounds** — Rich styling with borders, background colors, and more
|
|
25
|
+
- ♿ **Accessibility** — Screen reader support with ARIA attributes
|
|
26
|
+
- 🔄 **Hot Reloading** — Fast development with React DevTools support
|
|
27
|
+
- 📦 **TypeScript First** — Full TypeScript support with comprehensive type definitions
|
|
28
|
+
|
|
29
|
+
## 📦 Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Using npm
|
|
33
|
+
npm install tinky react
|
|
34
|
+
|
|
35
|
+
# Using yarn
|
|
36
|
+
yarn add tinky react
|
|
37
|
+
|
|
38
|
+
# Using pnpm
|
|
39
|
+
pnpm add tinky react
|
|
40
|
+
|
|
41
|
+
# Using bun
|
|
42
|
+
bun add tinky react
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 🚀 Quick Start
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { render, Box, Text } from "tinky";
|
|
49
|
+
|
|
50
|
+
function App() {
|
|
51
|
+
return (
|
|
52
|
+
<Box flexDirection="column" padding={1}>
|
|
53
|
+
<Text color="green" bold>
|
|
54
|
+
Hello, Tinky! 🎉
|
|
55
|
+
</Text>
|
|
56
|
+
<Text>Build beautiful CLIs with React</Text>
|
|
57
|
+
</Box>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
render(<App />);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 📚 Components
|
|
65
|
+
|
|
66
|
+
### Box
|
|
67
|
+
|
|
68
|
+
The `<Box>` component is a fundamental building block. It's like a `<div>` in the browser, supporting Flexbox and Grid layouts.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Box, Text } from "tinky";
|
|
72
|
+
|
|
73
|
+
// Flexbox layout
|
|
74
|
+
<Box flexDirection="row" gap={2}>
|
|
75
|
+
<Text>Left</Text>
|
|
76
|
+
<Text>Right</Text>
|
|
77
|
+
</Box>
|
|
78
|
+
|
|
79
|
+
// Grid layout
|
|
80
|
+
<Box display="grid" gridTemplateColumns="1fr 2fr 1fr" gap={1}>
|
|
81
|
+
<Text>Col 1</Text>
|
|
82
|
+
<Text>Col 2</Text>
|
|
83
|
+
<Text>Col 3</Text>
|
|
84
|
+
</Box>
|
|
85
|
+
|
|
86
|
+
// With borders and padding
|
|
87
|
+
<Box borderStyle="round" borderColor="cyan" padding={1}>
|
|
88
|
+
<Text>Styled Box</Text>
|
|
89
|
+
</Box>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Text
|
|
93
|
+
|
|
94
|
+
The `<Text>` component renders styled text with colors, bold, italic, and more.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { Text } from "tinky";
|
|
98
|
+
|
|
99
|
+
<Text color="blue">Blue text</Text>
|
|
100
|
+
<Text backgroundColor="red" color="white">Highlighted</Text>
|
|
101
|
+
<Text bold italic underline>Styled text</Text>
|
|
102
|
+
<Text color="#ff6600">Hex colors work too!</Text>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Static
|
|
106
|
+
|
|
107
|
+
The `<Static>` component renders static content that won't be updated. Perfect for logs and history.
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { Static, Text } from "tinky";
|
|
111
|
+
|
|
112
|
+
const logs = ["Log 1", "Log 2", "Log 3"];
|
|
113
|
+
|
|
114
|
+
<Static items={logs}>{(log, index) => <Text key={index}>{log}</Text>}</Static>;
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Transform
|
|
118
|
+
|
|
119
|
+
The `<Transform>` component allows you to transform the output of its children.
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { Transform, Text } from "tinky";
|
|
123
|
+
|
|
124
|
+
<Transform transform={(output) => output.toUpperCase()}>
|
|
125
|
+
<Text>hello</Text>
|
|
126
|
+
</Transform>;
|
|
127
|
+
// Renders: HELLO
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Newline & Spacer
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Box, Text, Newline, Spacer } from "tinky";
|
|
134
|
+
|
|
135
|
+
// Newline - adds vertical space
|
|
136
|
+
<Box flexDirection="column">
|
|
137
|
+
<Text>Line 1</Text>
|
|
138
|
+
<Newline count={2} />
|
|
139
|
+
<Text>Line 2</Text>
|
|
140
|
+
</Box>
|
|
141
|
+
|
|
142
|
+
// Spacer - flexible space in flex containers
|
|
143
|
+
<Box>
|
|
144
|
+
<Text>Left</Text>
|
|
145
|
+
<Spacer />
|
|
146
|
+
<Text>Right</Text>
|
|
147
|
+
</Box>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 🪝 Hooks
|
|
151
|
+
|
|
152
|
+
### useInput
|
|
153
|
+
|
|
154
|
+
Handle keyboard input in your components.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { useInput, useApp } from "tinky";
|
|
158
|
+
|
|
159
|
+
function MyComponent() {
|
|
160
|
+
const { exit } = useApp();
|
|
161
|
+
|
|
162
|
+
useInput((input, key) => {
|
|
163
|
+
if (key.escape) {
|
|
164
|
+
exit();
|
|
165
|
+
}
|
|
166
|
+
if (key.upArrow) {
|
|
167
|
+
// Handle up arrow
|
|
168
|
+
}
|
|
169
|
+
if (input === "q") {
|
|
170
|
+
exit();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return <Text>Press 'q' to quit</Text>;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### useApp
|
|
179
|
+
|
|
180
|
+
Access the app instance to control exit behavior.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { useApp } from "tinky";
|
|
184
|
+
|
|
185
|
+
function MyComponent() {
|
|
186
|
+
const { exit } = useApp();
|
|
187
|
+
|
|
188
|
+
// Exit with error
|
|
189
|
+
exit(new Error("Something went wrong"));
|
|
190
|
+
|
|
191
|
+
// Exit normally
|
|
192
|
+
exit();
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### useFocus & useFocusManager
|
|
197
|
+
|
|
198
|
+
Manage focus for interactive components.
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { useFocus, Box, Text } from "tinky";
|
|
202
|
+
|
|
203
|
+
function FocusableItem({ label }: { label: string }) {
|
|
204
|
+
const { isFocused } = useFocus();
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<Box borderStyle={isFocused ? "bold" : "single"}>
|
|
208
|
+
<Text color={isFocused ? "green" : "white"}>{label}</Text>
|
|
209
|
+
</Box>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### useStdin, useStdout, useStderr
|
|
215
|
+
|
|
216
|
+
Direct access to stdin, stdout, and stderr streams.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { useStdout, useEffect } from "tinky";
|
|
220
|
+
|
|
221
|
+
function MyComponent() {
|
|
222
|
+
const { write } = useStdout();
|
|
223
|
+
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
write("Hello from stdout!\n");
|
|
226
|
+
}, []);
|
|
227
|
+
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## 🎨 Styling
|
|
233
|
+
|
|
234
|
+
### Flexbox Properties
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
<Box
|
|
238
|
+
flexDirection="row" // row, row-reverse, column, column-reverse
|
|
239
|
+
justifyContent="center" // flex-start, flex-end, center, space-between, space-around
|
|
240
|
+
alignItems="center" // flex-start, flex-end, center, stretch
|
|
241
|
+
flexWrap="wrap" // nowrap, wrap, wrap-reverse
|
|
242
|
+
flexGrow={1}
|
|
243
|
+
flexShrink={0}
|
|
244
|
+
gap={2}
|
|
245
|
+
/>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Grid Properties
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
<Box
|
|
252
|
+
display="grid"
|
|
253
|
+
gridTemplateColumns="1fr 2fr 1fr"
|
|
254
|
+
gridTemplateRows="auto 1fr"
|
|
255
|
+
columnGap={1}
|
|
256
|
+
rowGap={1}
|
|
257
|
+
justifyItems="center"
|
|
258
|
+
alignItems="center"
|
|
259
|
+
/>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Border Styles
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
<Box borderStyle="single" /> // ┌─┐
|
|
266
|
+
<Box borderStyle="double" /> // ╔═╗
|
|
267
|
+
<Box borderStyle="round" /> // ╭─╮
|
|
268
|
+
<Box borderStyle="bold" /> // ┏━┓
|
|
269
|
+
<Box borderStyle="classic" /> // +--+
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Colors
|
|
273
|
+
|
|
274
|
+
Tinky supports multiple color formats:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
<Text color="red" /> // Named colors
|
|
278
|
+
<Text color="#ff6600" /> // Hex colors
|
|
279
|
+
<Text color="rgb(255, 102, 0)" /> // RGB colors
|
|
280
|
+
<Text color="ansi256:208" /> // ANSI 256 colors
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## 🔧 API Reference
|
|
284
|
+
|
|
285
|
+
### render(element, options?)
|
|
286
|
+
|
|
287
|
+
Render a React element to the terminal.
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { render } from "tinky";
|
|
291
|
+
|
|
292
|
+
const { unmount, waitUntilExit, rerender, clear } = render(<App />, {
|
|
293
|
+
stdout: process.stdout,
|
|
294
|
+
stdin: process.stdin,
|
|
295
|
+
stderr: process.stderr,
|
|
296
|
+
exitOnCtrlC: true,
|
|
297
|
+
patchConsole: true,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Wait for the app to exit
|
|
301
|
+
await waitUntilExit();
|
|
302
|
+
|
|
303
|
+
// Rerender with new props
|
|
304
|
+
rerender(<App newProp={true} />);
|
|
305
|
+
|
|
306
|
+
// Unmount the app
|
|
307
|
+
unmount();
|
|
308
|
+
|
|
309
|
+
// Clear the output
|
|
310
|
+
clear();
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### measureElement(ref)
|
|
314
|
+
|
|
315
|
+
Measure the dimensions of a rendered element.
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
import { measureElement, Box, useRef, useEffect } from "tinky";
|
|
319
|
+
|
|
320
|
+
function MyComponent() {
|
|
321
|
+
const ref = useRef(null);
|
|
322
|
+
|
|
323
|
+
useEffect(() => {
|
|
324
|
+
if (ref.current) {
|
|
325
|
+
const { width, height } = measureElement(ref.current);
|
|
326
|
+
console.log(`Size: ${width}x${height}`);
|
|
327
|
+
}
|
|
328
|
+
}, []);
|
|
329
|
+
|
|
330
|
+
return <Box ref={ref}>Content</Box>;
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## 🧪 Testing
|
|
335
|
+
|
|
336
|
+
Tinky uses Bun for testing. Run the test suite:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
bun test
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## 📄 License
|
|
343
|
+
|
|
344
|
+
MIT © [ByteLandTechnology](https://github.com/ByteLandTechnology)
|
|
345
|
+
|
|
346
|
+
## 🙏 Acknowledgements
|
|
347
|
+
|
|
348
|
+
- [Ink](https://github.com/vadimdemedes/ink) — The original React for CLIs
|
|
349
|
+
- [Taffy](https://github.com/DioxusLabs/taffy) — High-performance CSS layout engine
|
|
350
|
+
- [React](https://reactjs.org/) — The UI library that makes this possible
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">Tinky</h1>
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>使用 Taffy 布局引擎重新构想的 React CLI 框架</strong>
|
|
5
|
+
</p>
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="./README.md">English</a> · <a href="./README.ja-JP.md">日本語</a>
|
|
8
|
+
</p>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
[](https://www.npmjs.com/package/tinky)
|
|
15
|
+
|
|
16
|
+
Tinky 是一个现代化的基于 React 的框架,用于构建美观且交互式的命令行界面。它利用强大的 [Taffy](https://github.com/DioxusLabs/taffy) 布局引擎,在终端中提供 **CSS Flexbox 和 Grid** 布局支持。
|
|
17
|
+
|
|
18
|
+
## ✨ 特性
|
|
19
|
+
|
|
20
|
+
- 🎨 **React 组件** — 使用熟悉的 React 模式和 JSX 语法构建 CLI
|
|
21
|
+
- 📐 **Flexbox 和 Grid 布局** — 由 Taffy 驱动的完整 CSS Flexbox 和 CSS Grid 支持
|
|
22
|
+
- ⌨️ **键盘输入** — 内置处理键盘输入和焦点管理的 hooks
|
|
23
|
+
- 🎯 **焦点管理** — Tab/Shift+Tab 导航,可自定义焦点行为
|
|
24
|
+
- 🖼️ **边框和背景** — 丰富的边框、背景色等样式支持
|
|
25
|
+
- ♿ **无障碍访问** — 支持 ARIA 属性的屏幕阅读器
|
|
26
|
+
- 🔄 **热重载** — 支持 React DevTools 的快速开发体验
|
|
27
|
+
- 📦 **TypeScript 优先** — 完整的 TypeScript 支持和类型定义
|
|
28
|
+
|
|
29
|
+
## 📦 安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 使用 npm
|
|
33
|
+
npm install tinky react
|
|
34
|
+
|
|
35
|
+
# 使用 yarn
|
|
36
|
+
yarn add tinky react
|
|
37
|
+
|
|
38
|
+
# 使用 pnpm
|
|
39
|
+
pnpm add tinky react
|
|
40
|
+
|
|
41
|
+
# 使用 bun
|
|
42
|
+
bun add tinky react
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 🚀 快速开始
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { render, Box, Text } from "tinky";
|
|
49
|
+
|
|
50
|
+
function App() {
|
|
51
|
+
return (
|
|
52
|
+
<Box flexDirection="column" padding={1}>
|
|
53
|
+
<Text color="green" bold>
|
|
54
|
+
你好,Tinky!🎉
|
|
55
|
+
</Text>
|
|
56
|
+
<Text>用 React 构建漂亮的 CLI</Text>
|
|
57
|
+
</Box>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
render(<App />);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 📚 组件
|
|
65
|
+
|
|
66
|
+
### Box
|
|
67
|
+
|
|
68
|
+
`<Box>` 组件是基础构建块。它类似于浏览器中的 `<div>`,支持 Flexbox 和 Grid 布局。
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Box, Text } from "tinky";
|
|
72
|
+
|
|
73
|
+
// Flexbox 布局
|
|
74
|
+
<Box flexDirection="row" gap={2}>
|
|
75
|
+
<Text>左边</Text>
|
|
76
|
+
<Text>右边</Text>
|
|
77
|
+
</Box>
|
|
78
|
+
|
|
79
|
+
// Grid 布局
|
|
80
|
+
<Box display="grid" gridTemplateColumns="1fr 2fr 1fr" gap={1}>
|
|
81
|
+
<Text>列 1</Text>
|
|
82
|
+
<Text>列 2</Text>
|
|
83
|
+
<Text>列 3</Text>
|
|
84
|
+
</Box>
|
|
85
|
+
|
|
86
|
+
// 带边框和内边距
|
|
87
|
+
<Box borderStyle="round" borderColor="cyan" padding={1}>
|
|
88
|
+
<Text>样式化的 Box</Text>
|
|
89
|
+
</Box>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Text
|
|
93
|
+
|
|
94
|
+
`<Text>` 组件渲染带样式的文本,支持颜色、粗体、斜体等。
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { Text } from "tinky";
|
|
98
|
+
|
|
99
|
+
<Text color="blue">蓝色文本</Text>
|
|
100
|
+
<Text backgroundColor="red" color="white">高亮显示</Text>
|
|
101
|
+
<Text bold italic underline>样式化文本</Text>
|
|
102
|
+
<Text color="#ff6600">也支持十六进制颜色!</Text>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Static
|
|
106
|
+
|
|
107
|
+
`<Static>` 组件渲染不会更新的静态内容。非常适合日志和历史记录。
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { Static, Text } from "tinky";
|
|
111
|
+
|
|
112
|
+
const logs = ["日志 1", "日志 2", "日志 3"];
|
|
113
|
+
|
|
114
|
+
<Static items={logs}>{(log, index) => <Text key={index}>{log}</Text>}</Static>;
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Transform
|
|
118
|
+
|
|
119
|
+
`<Transform>` 组件允许你转换其子元素的输出。
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { Transform, Text } from "tinky";
|
|
123
|
+
|
|
124
|
+
<Transform transform={(output) => output.toUpperCase()}>
|
|
125
|
+
<Text>hello</Text>
|
|
126
|
+
</Transform>;
|
|
127
|
+
// 渲染结果: HELLO
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Newline 和 Spacer
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Box, Text, Newline, Spacer } from "tinky";
|
|
134
|
+
|
|
135
|
+
// Newline - 添加垂直空间
|
|
136
|
+
<Box flexDirection="column">
|
|
137
|
+
<Text>第一行</Text>
|
|
138
|
+
<Newline count={2} />
|
|
139
|
+
<Text>第二行</Text>
|
|
140
|
+
</Box>
|
|
141
|
+
|
|
142
|
+
// Spacer - flex 容器中的弹性空间
|
|
143
|
+
<Box>
|
|
144
|
+
<Text>左边</Text>
|
|
145
|
+
<Spacer />
|
|
146
|
+
<Text>右边</Text>
|
|
147
|
+
</Box>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 🪝 Hooks
|
|
151
|
+
|
|
152
|
+
### useInput
|
|
153
|
+
|
|
154
|
+
在组件中处理键盘输入。
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { useInput, useApp } from "tinky";
|
|
158
|
+
|
|
159
|
+
function MyComponent() {
|
|
160
|
+
const { exit } = useApp();
|
|
161
|
+
|
|
162
|
+
useInput((input, key) => {
|
|
163
|
+
if (key.escape) {
|
|
164
|
+
exit();
|
|
165
|
+
}
|
|
166
|
+
if (key.upArrow) {
|
|
167
|
+
// 处理上箭头
|
|
168
|
+
}
|
|
169
|
+
if (input === "q") {
|
|
170
|
+
exit();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return <Text>按 'q' 退出</Text>;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### useApp
|
|
179
|
+
|
|
180
|
+
访问应用实例以控制退出行为。
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { useApp } from "tinky";
|
|
184
|
+
|
|
185
|
+
function MyComponent() {
|
|
186
|
+
const { exit } = useApp();
|
|
187
|
+
|
|
188
|
+
// 带错误退出
|
|
189
|
+
exit(new Error("出错了"));
|
|
190
|
+
|
|
191
|
+
// 正常退出
|
|
192
|
+
exit();
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### useFocus 和 useFocusManager
|
|
197
|
+
|
|
198
|
+
管理交互式组件的焦点。
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { useFocus, Box, Text } from "tinky";
|
|
202
|
+
|
|
203
|
+
function FocusableItem({ label }: { label: string }) {
|
|
204
|
+
const { isFocused } = useFocus();
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<Box borderStyle={isFocused ? "bold" : "single"}>
|
|
208
|
+
<Text color={isFocused ? "green" : "white"}>{label}</Text>
|
|
209
|
+
</Box>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### useStdin、useStdout、useStderr
|
|
215
|
+
|
|
216
|
+
直接访问 stdin、stdout 和 stderr 流。
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
import { useStdout, useEffect } from "tinky";
|
|
220
|
+
|
|
221
|
+
function MyComponent() {
|
|
222
|
+
const { write } = useStdout();
|
|
223
|
+
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
write("来自 stdout 的问候!\n");
|
|
226
|
+
}, []);
|
|
227
|
+
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## 🎨 样式
|
|
233
|
+
|
|
234
|
+
### Flexbox 属性
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
<Box
|
|
238
|
+
flexDirection="row" // row, row-reverse, column, column-reverse
|
|
239
|
+
justifyContent="center" // flex-start, flex-end, center, space-between, space-around
|
|
240
|
+
alignItems="center" // flex-start, flex-end, center, stretch
|
|
241
|
+
flexWrap="wrap" // nowrap, wrap, wrap-reverse
|
|
242
|
+
flexGrow={1}
|
|
243
|
+
flexShrink={0}
|
|
244
|
+
gap={2}
|
|
245
|
+
/>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Grid 属性
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
<Box
|
|
252
|
+
display="grid"
|
|
253
|
+
gridTemplateColumns="1fr 2fr 1fr"
|
|
254
|
+
gridTemplateRows="auto 1fr"
|
|
255
|
+
columnGap={1}
|
|
256
|
+
rowGap={1}
|
|
257
|
+
justifyItems="center"
|
|
258
|
+
alignItems="center"
|
|
259
|
+
/>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 边框样式
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
<Box borderStyle="single" /> // ┌─┐
|
|
266
|
+
<Box borderStyle="double" /> // ╔═╗
|
|
267
|
+
<Box borderStyle="round" /> // ╭─╮
|
|
268
|
+
<Box borderStyle="bold" /> // ┏━┓
|
|
269
|
+
<Box borderStyle="classic" /> // +--+
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 颜色
|
|
273
|
+
|
|
274
|
+
Tinky 支持多种颜色格式:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
<Text color="red" /> // 命名颜色
|
|
278
|
+
<Text color="#ff6600" /> // 十六进制颜色
|
|
279
|
+
<Text color="rgb(255, 102, 0)" /> // RGB 颜色
|
|
280
|
+
<Text color="ansi256:208" /> // ANSI 256 颜色
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## 🔧 API 参考
|
|
284
|
+
|
|
285
|
+
### render(element, options?)
|
|
286
|
+
|
|
287
|
+
将 React 元素渲染到终端。
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { render } from "tinky";
|
|
291
|
+
|
|
292
|
+
const { unmount, waitUntilExit, rerender, clear } = render(<App />, {
|
|
293
|
+
stdout: process.stdout,
|
|
294
|
+
stdin: process.stdin,
|
|
295
|
+
stderr: process.stderr,
|
|
296
|
+
exitOnCtrlC: true,
|
|
297
|
+
patchConsole: true,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// 等待应用退出
|
|
301
|
+
await waitUntilExit();
|
|
302
|
+
|
|
303
|
+
// 使用新 props 重新渲染
|
|
304
|
+
rerender(<App newProp={true} />);
|
|
305
|
+
|
|
306
|
+
// 卸载应用
|
|
307
|
+
unmount();
|
|
308
|
+
|
|
309
|
+
// 清除输出
|
|
310
|
+
clear();
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### measureElement(ref)
|
|
314
|
+
|
|
315
|
+
测量已渲染元素的尺寸。
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
import { measureElement, Box, useRef, useEffect } from "tinky";
|
|
319
|
+
|
|
320
|
+
function MyComponent() {
|
|
321
|
+
const ref = useRef(null);
|
|
322
|
+
|
|
323
|
+
useEffect(() => {
|
|
324
|
+
if (ref.current) {
|
|
325
|
+
const { width, height } = measureElement(ref.current);
|
|
326
|
+
console.log(`尺寸: ${width}x${height}`);
|
|
327
|
+
}
|
|
328
|
+
}, []);
|
|
329
|
+
|
|
330
|
+
return <Box ref={ref}>内容</Box>;
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## 🧪 测试
|
|
335
|
+
|
|
336
|
+
Tinky 使用 Bun 进行测试。运行测试套件:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
bun test
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## 📄 许可证
|
|
343
|
+
|
|
344
|
+
MIT © [ByteLandTechnology](https://github.com/ByteLandTechnology)
|
|
345
|
+
|
|
346
|
+
## 🙏 致谢
|
|
347
|
+
|
|
348
|
+
- [Ink](https://github.com/vadimdemedes/ink) — 原版 React CLI 框架
|
|
349
|
+
- [Taffy](https://github.com/DioxusLabs/taffy) — 高性能 CSS 布局引擎
|
|
350
|
+
- [React](https://reactjs.org/) — 使这一切成为可能的 UI 库
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type representing the target of the color application (foreground or
|
|
3
|
+
* background).
|
|
4
|
+
*/
|
|
5
|
+
type ColorType = "foreground" | "background";
|
|
6
|
+
/**
|
|
7
|
+
* Applies a color to a string using Chalk.
|
|
8
|
+
* Supports named colors, hex codes, RGB values, and ANSI-256 color codes.
|
|
9
|
+
*
|
|
10
|
+
* @param str - The string to colorize.
|
|
11
|
+
* @param color - The color to apply.
|
|
12
|
+
* @param type - Whether to apply the color to the foreground or background.
|
|
13
|
+
* @returns The colorized string.
|
|
14
|
+
*/
|
|
15
|
+
export declare const colorize: (str: string, color: string | undefined, type: ColorType) => string;
|
|
16
|
+
export {};
|