Terminal
API reference for terminal utilities
Hooks and components for reactive terminal dimensions, OS-level focus detection, and handing terminal control to external programs.
These are imported from giggles/terminal — a separate entry point not included in the main bundle.
import { useTerminalSize, useTerminalFocus, useShellOut, useSpawn } from 'giggles/terminal';API Reference
useTerminalSize()
Returns reactive terminal dimensions that update on resize.
function useTerminalSize(): TerminalSizeReturns: TerminalSize
Prop
Type
useTerminalFocus()
Detects when the terminal window gains or loses OS-level focus.
function useTerminalFocus(callback: (focused: boolean) => void): voidOpts into ANSI focus reporting (\x1b[?1004h). The terminal emits a sequence when the user switches to another app and back. Not all terminal emulators support this — test for your target environment.
Prop
Type
useShellOut()
Hands terminal control to an external program and reclaims it when the process exits.
function useShellOut(): { run: (command: string) => Promise<{ exitCode: number }> }run() handles the full sequence: leaves the alternate screen, releases stdin/stdout to the child process with stdio: 'inherit', waits for exit, then reclaims control and repaints. Getting this sequence wrong leaves the terminal in a broken state — use this instead of spawning processes manually.
run() never throws on non-zero exit codes. Check exitCode in the result if you need to act on failure.
Prop
Type
useSpawn()
Spawns a child process and streams its output into React state while the TUI stays live.
function useSpawn(): SpawnHandleUnlike useShellOut, the TUI keeps rendering while the process runs — output arrives incrementally via output. Call run() to spawn a process; calling it again while a process is already running kills the previous one first. The process is killed automatically on unmount.
Returns: SpawnHandle
Prop
Type
SpawnOutputLine
Prop
Type
Options
All options from Node's child_process.spawn are accepted, plus:
Prop
Type
Examples
Responsive layout
import { useTerminalSize } from 'giggles/terminal';
import { Box } from 'ink';
function App() {
const { columns } = useTerminalSize();
return (
<Box>
{columns > 80 && <Sidebar />}
<MainContent />
</Box>
);
}Pause on blur
import { useTerminalFocus } from 'giggles/terminal';
import { useState } from 'react';
import { Text } from 'ink';
function Timer() {
const [paused, setPaused] = useState(false);
useTerminalFocus((focused) => {
setPaused(!focused);
});
return <Text>{paused ? 'Paused' : 'Running'}</Text>;
}Stream command output
import { useFocusScope, useKeybindings } from 'giggles';
import { useSpawn } from 'giggles/terminal';
import { Box, Text } from 'ink';
function DockerLogs({ container }: { container: string }) {
const scope = useFocusScope();
const { output, running, error, run, kill } = useSpawn();
useKeybindings(scope, {
r: () => run('docker', ['logs', '-f', container], { pty: true }),
k: kill,
});
return (
<Box flexDirection="column">
{error && <Text color="red">{error.message}</Text>}
{output.map((line, i) => (
<Text key={i}>{line.data}</Text>
))}
{running && <Text dimColor>Tailing logs… (k to stop)</Text>}
{!running && !error && <Text dimColor>r to tail logs</Text>}
</Box>
);
}Open $EDITOR
import { useFocusScope, useKeybindings } from 'giggles';
import { useShellOut } from 'giggles/terminal';
import { Text } from 'ink';
function FileViewer({ path }: { path: string }) {
const scope = useFocusScope();
const shell = useShellOut();
useKeybindings(scope, {
e: async () => {
await shell.run(`${process.env.EDITOR ?? 'vim'} ${path}`);
},
});
return <Text dimColor>Press e to edit {path}</Text>;
}