Loading skill
Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate Ink (React for CLI) components for terminal UIs with hooks, state management, and layout components.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 42% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 14% | 0% |
| case-12 | ✗→✓ | ▲ Improved | -16% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 55% | 0% |
Generate Ink (React) components for terminal UIs.
Invoke this skill when you need to:
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | projectName | string | Yes | Project name | | components | array | Yes | Component definitions | | includeHooks | boolean | No | Generate custom hooks |
json{ "components": [ { "name": "SelectList", "type": "interactive", "props": ["items", "onSelect"], "state": ["selectedIndex"] } ] }
tsximport React, { useState, useCallback } from 'react'; import { Box, Text, useInput, useApp } from 'ink'; interface SelectListProps { items: string[]; onSelect: (item: string, index: number) => void; } export const SelectList: React.FC<SelectListProps> = ({ items, onSelect }) => { const [selectedIndex, setSelectedIndex] = useState(0); const { exit } = useApp(); useInput((input, key) => { if (key.upArrow) { setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1)); } else if (key.downArrow) { setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0)); } else if (key.return) { onSelect(items[selectedIndex], selectedIndex); } else if (input === 'q' || key.escape) { exit(); } }); return ( <Box flexDirection="column"> {items.map((item, index) => ( <Box key={item}> <Text color={index === selectedIndex ? 'green' : undefined}> {index === selectedIndex ? '> ' : ' '} {item} </Text> </Box> ))} <Box marginTop={1}> <Text dimColor>Use arrow keys to navigate, Enter to select, q to quit</Text> </Box> </Box> ); };
tsximport React, { useState } from 'react'; import { Box, Text, useInput } from 'ink'; interface TextInputProps { placeholder?: string; onSubmit: (value: string) => void; mask?: string; } export const TextInput: React.FC<TextInputProps> = ({ placeholder = '', onSubmit, mask, }) => { const [value, setValue] = useState(''); const [cursor, setCursor] = useState(0); useInput((input, key) => { if (key.return) { onSubmit(value); return; } if (key.backspace || key.delete) { setValue((prev) => prev.slice(0, -1)); setCursor((prev) => Math.max(0, prev - 1)); return; } if (!key.ctrl && !key.meta && input) { setValue((prev) => prev + input); setCursor((prev) => prev + 1); } }); const displayValue = mask ? mask.repeat(value.length) : value; return ( <Box> <Text> {displayValue || <Text dimColor>{placeholder}</Text>} <Text backgroundColor="white"> </Text> </Text> </Box> ); };
tsximport React, { useState, useEffect } from 'react'; import { Text } from 'ink'; const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; interface SpinnerProps { label?: string; } export const Spinner: React.FC<SpinnerProps> = ({ label }) => { const [frame, setFrame] = useState(0); useEffect(() => { const timer = setInterval(() => { setFrame((prev) => (prev + 1) % frames.length); }, 80); return () => clearInterval(timer); }, []); return ( <Text> <Text color="green">{frames[frame]}</Text> {label && <Text> {label}</Text>} </Text> ); };
tsximport { useState, useEffect, useCallback } from 'react'; interface AsyncState<T> { data: T | null; loading: boolean; error: Error | null; } export function useAsync<T>( asyncFn: () => Promise<T>, deps: any[] = [] ): AsyncState<T> & { refetch: () => void } { const [state, setState] = useState<AsyncState<T>>({ data: null, loading: true, error: null, }); const execute = useCallback(async () => { setState({ data: null, loading: true, error: null }); try { const data = await asyncFn(); setState({ data, loading: false, error: null }); } catch (error) { setState({ data: null, loading: false, error: error as Error }); } }, deps); useEffect(() => { execute(); }, [execute]); return { ...state, refetch: execute }; }
json{ "dependencies": { "ink": "^4.0.0", "react": "^18.0.0" }, "devDependencies": { "@types/react": "^18.0.0", "ink-testing-library": "^3.0.0" } }
Other measured skills in the registry, with their headline benchmark lift.