giggles

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
import { useTerminalSize, useTerminalFocus, useShellOut, useSpawn } from 'giggles/terminal';

API Reference

useTerminalSize()

Returns reactive terminal dimensions that update on resize.

Type Signature
function useTerminalSize(): TerminalSize

Returns: TerminalSize

Prop

Type

useTerminalFocus()

Detects when the terminal window gains or loses OS-level focus.

Type Signature
function useTerminalFocus(callback: (focused: boolean) => void): void

Opts 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.

Type Signature
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.

Type Signature
function useSpawn(): SpawnHandle

Unlike 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

Responsive Sidebar
import { useTerminalSize } from 'giggles/terminal';
import { Box } from 'ink';

function App() {
  const { columns } = useTerminalSize();

  return (
    <Box>
      {columns > 80 && <Sidebar />}
      <MainContent />
    </Box>
  );
}

Pause on blur

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

Docker Logs
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

Edit a File
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>;
}

On this page