npm package: @morgs32/ink-steps
Description: Ink components for step-by-step CLI flows. The public API is exported from `src/index.ts`. The sub-entry `src/MyMultiSelect/index.ts` re-exports the multi-select and its types.
Latest version: 0.0.14

# ink-steps

Ink components for step-by-step CLI flows. The public API is exported from `src/index.ts`. The sub-entry `src/MyMultiSelect/index.ts` re-exports the multi-select and its types.

## ProcedureStep

`ProcedureStep` and its helpers model a procedural step with a status-driven display. It provides a context that children read to decide what to render.

Status values (`ProcedureStepStatus`): `error`, `loading`, `prompt`, `saving`, `success`.

Exports:

- `ProcedureStep` - wraps a step, renders a vertical guide line, and provides the status context.
- `ProcedureStepContext` / `useProcedureStepContext` - context + hook; the hook throws if used outside a `ProcedureStep`.
- `ProcedureStepPrompt` - renders a yellow diamond and children when status is `prompt`.
- `ProcedureStepLoading` - renders a spinner (and optional message) when status is `loading`.
- `ProcedureStepSuccess` - renders a green diamond and children when status is `success`.
- `ProcedureStepError` - renders a red error line when status is `error` and an error is provided.
- `ProcedureNextStep` - renders children only when status is `success` (for chaining steps).
- `useProgram` / `UseProgramResult` - runs an `effect` program once on mount and maps it to `{ status, data, error }`.

How it shows up in the `ska` add flow:

- `../ska/src/commands/add.tsx` composes a sequence of add steps (for example `FindSkills`, `GetLocks`, `UpdateLock`). Those steps use `ProcedureStep` to show loading/success/error while still returning render-prop children to continue the chain.
- `../ska/src/add/FindSkills.tsx` uses `useProgram` + `ProcedureStep` to report the status of reading skills.
- `../ska/src/add/SelectTargetLock.tsx` uses `ProcedureStepPrompt` for an interactive choice and `ProcedureStepSuccess` once chosen.

Example (trimmed from `../ska/src/add/FindSkills.tsx`):

```tsx
const { data, error, status } = useProgram({
  fetcher: () => readSkillsFromCache(cachePath),
});

return (
  <ProcedureStep status={status}>
    <ProcedureStepError error={error} />
    {data && (
      <ProcedureStepSuccess>
        <Text>Skills found ({data.length})</Text>
      </ProcedureStepSuccess>
    )}
    <ProcedureStepLoading message="Reading skills..." />
    <ProcedureNextStep>{data ? children(data) : null}</ProcedureNextStep>
  </ProcedureStep>
);
```

## PromptStep

`PromptStep` models an interactive step that the user must submit. It uses a render prop to expose a `submit` callback and a context to coordinate the prompt/success views.

Status values (`PromptStatus`): `inputting`, `submitted`.

Exports:

- `PromptStep` - wraps a step and provides `{ submit }` to its children.
- `PromptStepContext` / `usePromptStepContext` - context + hook; the hook throws if used outside a `PromptStep`.
- `PromptStepPrompt` - renders a yellow diamond and children while inputting.
- `PromptStepSuccess` - renders a green diamond and children once submitted.
- `PromptNextStep` - renders children only once submitted.

How it shows up in the `ska` add flow:

- `../ska/src/add/SelectSkills.tsx` uses `PromptStep` with `MyMultiSelect`. Once the user submits their selection, it calls `submit()` to switch the UI into the success state and lets the next step render.

Example (trimmed from `../ska/src/add/SelectSkills.tsx`):

```tsx
<PromptStep>
  {({ submit }) => (
    <>
      <PromptStepPrompt>
        <MyMultiSelect
          options={filteredOptions}
          onSubmit={(values) => {
            setSelectedValues(values);
            submit();
          }}
        />
      </PromptStepPrompt>
      <PromptStepSuccess>
        <Text>{selectionSummary}</Text>
      </PromptStepSuccess>
      <PromptNextStep>{children(selectedSkills)}</PromptNextStep>
    </>
  )}
</PromptStep>
```

## Miscellaneous

### MyMultiSelect

A keyboard-driven multi-select list with scrollable options and optional highlight text.

Exports:

- `MyMultiSelect` - renders options, handles focus/selection, and calls `onSubmit` on Enter.
- `MyMultiSelectProps` - props for the component.
- `MyMultiSelectStateChange` - `{ focusedValue, value }` emitted via `onStateChange`.
- `Option` - `{ label, value }` option shape.

Behavior notes:

- Up/Down arrows move focus, Space toggles the focused option, Tab selects all or none, Enter triggers `onSubmit`.
- `highlightText` is rendered in a different style inside matching labels.
- `visibleOptionCount` controls the scroll window size.

### ConditionalStep / renderConditionalStep

Utility to optionally wrap children. If `if` is true, `then(children)` is returned; otherwise the children render as-is. `renderConditionalStep` is the same logic as a function.

### Exit

Ink component that exits the app after 5 seconds (`useApp().exit()`). Returns `null` and is useful at the end of flows.

### ErrorBoundary

Class-based error boundary for Ink. It logs the error and component stack to stderr and renders a user-facing error message when a child throws.