open-index/open-npm huggingface dataset:
npm package: @matthamlin/re-load
Description: Re-load is a react component for handling (psuedo-)async loading states.
Author: Matthew Hamlin
Latest version: 1.0.0

# re-load

Re-load is a react component for handling (psuedo-)async loading states.

## Example

```jsx
import Reload from '@matthamlin/re-load'

let src = 'https://my-cool-img-resource.com/img.jpg'

render(
  <Reload
    propName="src"
    src={src}
    resolver={() =>
      new Promise((resolve, reject) => {
        let img = new Image()
        img.onload = () => resolve(img)
        img.onerror = () => reject('Failed to load the image resource :(')
        img.src = src
      })
    }
  >
    {({ src, meta }) =>
      src === null ? (
        <p> Image Loading ... </p>
      ) : (
        <img
          src={src}
          height={meta.height}
          width={meta.width}
          alt="My Cool Image"
        />
      )
    }
  </Reload>,
)
```

## API

### Props

- `propName` [Required] The name of the prop that will be resolved
- `resolver` An optional function that returns a promise, the value resolved will be set to the `meta` key in the callback, and the value rejected will be set to the `error` key in the callback
- `delay` An optional delay used if `resolver` isn't provided
- `children` [Required] The callback render function for Reload, it is called with an object containing the following:

```js
{
  [this.props.propName]: null | any,
  meta?: any,
  error?: any
}
```
npm package: @matthamlin/react-code-editor
Description: `@matthamlin/react-code-editor` is an opinionated package built on top of React Hooks and react-simple-code-editor.
Author: Matthew Hamlin
Latest version: 1.0.1

# React Code Editor

`@matthamlin/react-code-editor` is an opinionated package built on top of React Hooks and react-simple-code-editor.

It implements a lightweight code editor component that supports the following enhancements:

- Line commenting via `Cmd + /` or `Ctrl + /`
- Format on Save via `Cmd + s` or `Ctrl + s` - with `formatOnSave` prop
- Format code via `Cmd + f` or `Ctrl + f`
- maybe more soon???

## Usage

```jsx
import React from 'react'
import Editor from '@matthamlin/react-code-editor'
import { highlight, languages } from 'prismjs/components/prism-core'
import 'prismjs/components/prism-clike'
import 'prismjs/components/prism-javascript'
import prettier from 'prettier/standalone'
import babelPlugin from 'prettier/parser-babylon'

const code = `import React from "react";
import ReactDOM from "react-dom";
function App() {
  return (
    <h1>Hello world</h1>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));`

function App() {
  return (
    <Editor
      initialValue={code}
      highlight={code => highlight(code, languages.jsx)}
      padding={10}
      style={{
        fontFamily: '"Fira code", "Fira Mono", monospace',
        fontSize: 12,
      }}
      formatOnSave
      formatCode={code =>
        prettier.format(code, { parser: 'babel', plugins: [babelPlugin] })
      }
    />
  )
}
```
npm package: @matthamlin/react-error-boundary
Description: A simple error boundary component for React 16+
Author: Matthew Hamlin
Latest version: 1.0.0

# React-Error-Boundary 🚯

An unopinionated `<ErrorBoundary />` React component for React 16+.

```JSX
<div>
  <ErrorBoundary
    onCaughtError={logError}
  >
    {error => error
      ? (
        <h1>Oh no an error occurred!</h1>
      )
      : (
        <Application />
      )
    }
  </ErrorBoundary>
</div>
```

## Props:

* `onCaughtError` method called after `<ErrorBoundary />` catches an error.
  * type `function` 
  * Called with one argument, the `error` object thrown
* `children` function called inside render
  * Called with the error object, will be null initially
  * This is the only thing returned from render, so you have full control over rendering
npm package: @matthamlin/react-lightbox
Description: A simple react lightbox component for images
Author: Matthew Hamlin
Latest version: 4.0.0

# React Lightbox

This is a simple Lightbox component for React (v16 and above).

### To use in your project:

```javascript
import Lightbox from '@matthamlin/react-lightbox';

require('@matthamlin/react-lightbox/src/Lightbox.css');

...

render(
  <Lightbox
    images={
      [
        {
          img: 'path/to/image.jpg',
          caption: 'Image Caption'
        },
        {
          img: 'path/to/another_image.jpg',
          caption: 'Another Image Caption'
        }
      ]
    }
  />,
  root
);
```

### Requirements:

* React
* CSSNext (color function, this can be changed out to whatever color you want however)
npm package: @matthamlin/react-media
Description: This component is a simple wrapper around the `window.matchMedia` functionality, as such it will only work (as intended) in browsers at or above the versions from this table: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia.
Author: Matthew Hamlin
Latest version: 1.1.0

# MatchMedia Component

This component is a simple wrapper around the `window.matchMedia` functionality,
as such it will only work (as intended) in browsers at or above the versions from this
table: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia.

## Example:

```Javascript
import MatchMedia from '@matthamlin/react-media';

const MyComponent = props => (
  <div>
    <MatchMedia media="(min-width: 400px)">
      {matches => matches ? (<MatchedComponent />) : (<NonMatchedComponent />)}
    </MatchMedia>
  </div>
);
```

## API:

MatchMedia accepts two props, the first being `media` which is a media query string, the
second is children which is a callback function that accepts one parameter that is a
boolean if the media query has matched or not.

## Contributing:

If you have found an issue or want to add features/tests feel free to open a PR
and explain what the PR is fixing/adding and I will work with you in getting that fix
merged into the master branch!

## Questions?

Feel free to open an issue and I will get back to you!
npm package: @matthamlin/react-property-controls
Description: Components for rendering React component playgrounds.
Author: Matthew Hamlin
Latest version: 1.0.1

# React Property Controls

Components for rendering React component playgrounds.

### Example

```jsx
import {
  makeControls,
  stringType,
  numberType,
  boolType
} from '@wayfair/react-property-controls';
// Could be local inputs to your project, or some UI library from NPM
import {TextInput, NumberInput, Toggle} from '../component-library';

function MyComponent({priceLabel, price, hidePriceLabel}) {
  return (
    <span>
      This product costs: {hidePriceLabel ? null : priceLabel}
      {price}
    </span>
  );
}

// Define your controls for the component
// these are like prop-types for the component, but allow you to specify additional
// data for the rendered inputs
// In this case we specify a label for our inputs for each control, as well as some defaultValues
const controls = {
  priceLabel: {
    type: stringType,
    label: 'The price label (i.e. $) of the product',
    isHidden(props) {
      // props.state is our control state coming from the StateContainer component
      return !!props.state.hidePriceLabel;
    },
    defaultValue: '$'
  },
  price: {
    type: numberType,
    label: 'The price of the product',
    defaultValue: 0
  },
  hidePriceLabel: {
    type: boolType,
    label: 'If the price label should be hidden',
    defaultValue: false
  }
};

const {PropertyControls, values} = makeControls({
  controls,
  StringInput: TextInput,
  NumberInput,
  BoolInput: Toggle
});

// Simple state container
class StateContainer extends React.Component {
  state = values;
  update = name => value => this.setState({[name]: value});
  render() {
    return this.props.children({
      state: this.state,
      update: this.update
    });
  }
}
// Render controls and the component
ReactDOM.render(
  <StateContainer>
    {({state, update}) => (
      <React.Fragment>
        <PropertyControls state={state} update={update} />
        <MyComponent {...state} />
      </React.Fragment>
    )}
  </StateContainer>
);
```

### API

For more information on the API of this package, refer to the documentation in the docs directory.

### Development Guide

If you are working on this project locally here are the steps you can follow to
setup the package:

1. clone the repo
2. `npm install` the dependencies
3. `cd sandbox` and `npm install`
4. `npm run build` to build the library to the dist directory (you should only
   need to run this once, the script will watch for changes to the `src`
   directory)
5. `npm run test` to run tests

To test your changes locally within the sandbox, you will need to link the
package locally and then install it from the link:

```sh
# in the root directory
npm run build-once
npm link
cd ./sandbox
npm link @wayfair/react-property-controls
```
npm package: @matthamlin/react-server-dom-esm
Description: React Server Components bindings for DOM using ESM. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.
Latest version: 0.0.0-experimental-594fb5e9-20250811

# react-server-dom-esm

Experimental React Flight bindings for DOM using ESM.

**Use it at your own risk.**
npm package: @matthamlin/reroute-browser
Description: A collection of routing components and hooks for composing web react applications.
Author: Matthew Hamlin
Latest version: 1.3.0

# `@matthamlin/reroute-browser`

A collection of routing components and hooks for composing web react applications.

## Install

```sh
yarn add @matthamlin/reroute-browser
# Or
npm install @matthamlin/reroute-browser
```

## API

```jsx
import {
  BrowserRouter,
  Route,
  Link,
  useLink,
  useRoute,
} from '@matthamlin/reroute-browser'

// useLink and useRoute are the same exports from '@matthamlin/reroute-core'

render(
  <BrowserRouter>
    <Route path="/hello-world">
      {({ match }) =>
        match && (
          <div>
            <marquee>Hello World!</marquee>
            <Link to="/">Go Back</Link>
          </div>
        )
      }
    </Route>
    <Link to="/hello-world">Go to Greeting</Link>
  </BrowserRouter>,
)
```

## Demo

Check out the [Codesandbox Demo](https://codesandbox.io/s/n96xx2p4yp).

## Testing

If you are testing components that use the Route or Link component, you may need to mount your
component with a test version of the Router. To do this, you can either:

1. Render inside a `<BrowserRouter>` or,
2. Render within a `<Router>` from `@matthamlin/reroute-core`

#### Option 1

The default BrowserRouter from `@matthamlin/reroute-browser` should work as expected within your test
environment, as long as you define the `window` and `document` APIs. If you are using Jest / a
testing framework that uses JSDom then you should be all set.

#### Option 2

If you want more control over the current Router state, for example mounting your application during
a test at a nested pathname, then you can use the `<Router>` from `@matthamlin/reroute-core` and provide
it a function to it's `createHistory` prop. Here we are using the `createMemoryHistory` function
from the `history` module on NPM.

```jsx
import { createMemoryHistory } from 'history'
import { Router } from '@matthamlin/reroute-core'

render(
  <Router createHistory={createMemoryHistory}>
    <FeatureComponent />
  </Router>,
)
```

#### Mounting with A Default Entry

```jsx
import { createMemoryHistory } from 'history'
import { Router } from '@matthamlin/reroute-core'

render(
  <Router
    createHistory={() =>
      createMemoryHistory({
        initialEntries: ['/foo'],
      })
    }
  >
    <Route path="/foo">
      {({ match }) => match && <>This will render initially</>}
    </Route>
  </Router>,
)
```

Check out the `history` modules documentation [here](https://www.npmjs.com/package/history#usage)
for more information about how you can configure different history variations.
npm package: @matthamlin/reroute-core
Description: This package maintains the core hooks for the Reroute library.
Author: Matthew Hamlin
Latest version: 1.2.0

# Reroute Core

This package maintains the core hooks for the Reroute library.

These can be manually consumed within feature applications, or they can be consumed through the
[`@matthamlin/reroute-browser`](https://github.com/hamlim/projects/tree/master/packages/reroute-browser)
package via common components like `Link` and `Route`.

## Install

```sh
yarn add @matthamlin/reroute-core
# Or
npm install @matthamlin/reroute-core
```

## API

```jsx
import { createBrowserHistory } from 'history'
import { Router, useRoute, useLink } from '@matthamlin/reroute-core'

function UserRoute({ userId }) {
  let { match } = useRoute(`/user-${userId}`)
  if (match) {
    return <User />
  }
  return null
}

function Avatar({ userId }) {
  let getLinkProps = useLink(`/user-${userId}`)
  return (
    <a {...getLinkProps()}>
      <img src={`/users/${userId}.png`} alt={`${users[userId]}`} />
    </a>
  )
}

render(
  <Router createHistory={() => createBrowserHistory()}>
    <main>
      <UserRoute userId="1" />
    </main>
    <aside>
      <Avatar userId="1" />
    </aside>
  </Router>,
)
```

## Testing

If you are testing components that use the `useRoute` or `useLink` hooks, you may need to mount your
component with a test version of the Router. To do this, you can render your components within a
custom Router component.

#### Mounting your Component

If you want more control over the current Router state, for example mounting your application during
a test at a nested pathname, then you can use the `<Router>` from `@matthamlin/reroute-core` and provide
it a function to it's `createHistory` prop. Here we are using the `createMemoryHistory` function
from the `history` module on NPM.

```jsx
import { createMemoryHistory } from 'history'
import { Router } from '@matthamlin/reroute-core'

render(
  <Router createHistory={createMemoryHistory}>
    <FeatureComponent />
  </Router>,
)
```

#### If you want to default the Router to a particular pathname

```jsx
import { createMemoryHistory } from 'history'
import { Router } from '@reroute/core'

render(
  <Router
    createHistory={() =>
      createMemoryHistory({
        initialEntries: ['/foo'],
      })
    }
  >
    <Route path="/foo">{({ match }) => match && <>This will render initially</>}</Route>
  </Router>,
)
```

Check out the `history` modules documentation [here](https://www.npmjs.com/package/history#usage)
for more information about how you can configure different history variations.
npm package: @matthamlin/simple-cache
Description: A simple react-cache implementation that works for both Server Components and Client Components!
Author: Matt Hamlin
Latest version: 2.0.2

# `@matthamlin/simple-cache`

A simple react-cache implementation that works for both Server Components and
Client Components!

## API

```tsx
// For use on the client, or in client components during SSR
import { useCache } from '@matthamlin/simple-cache/client'

let cache = new Map()

interface Result {
  something: boolean
}

function useFetch(endpoint) {
  return useCache<Result>(cache, endpoint, () =>
    fetch(endpoint).then((data) => data.json()),
  )
}
```

Or with Server Components (experimental):

```tsx
import { useCache } from '@matthamlin/simple-cache/server'

let cache = new Map()

interface Result {
  something: boolean
}

function useData(endpoint) {
  return useCache<Result>(cache, endpoint, () =>
    fetch(endpoint).then((res) => res.json()),
  )
}
```

### `useCache` Arguments:

- `cache` - A cache, expects the same api as a `Map`
- `key` - A unique key (string) to index the cache by
- `miss` - A function that returns a promise to suspend (function)

### Recipes:

#### Sharing a cache between the server and the client:

<!-- prettier-ignore -->
> **Note** 
> I haven't done extensive testing with this approach, but it could
> work!

The `server` entrypoint also exports a `serializeCache` function that takes in
the `cache` and returns a stringified representation of the cache. It's up to
you to pass that data down to the client, one possible approach is a `<script>`
tag injected alongside the component that suspended on the server!

On the client, you can import `deserializeCache` from the `client` entrypoint
and pass that stringified representation to it to get back the `cache` which you
can pass through to all `useCache` calls.

As long as the keys are equivalent, this should allow you to avoid double
fetching on the client.

<!-- prettier-ignore -->
> **Note**
> This doesn't yet work for _pending_ `useCache` calls, not that I'd
> expect the server to flush those to the client 🤔

### Built Using:

- Typescript
- Babel
npm package: @matthamlin/srvr
Description: A simple React server rendering handler
Author: Matt Hamlin
Latest version: 0.0.2

# TODO

Make sure to update this with the right documentation!

## Installation:

```sh
yarn add TODO
```

## Usage:

```tsx
// TODO
```

## Contributing:

### `build`

```sh
yarn turbo run build
```

### `test`

```sh
yarn turbo run test
```

### Tools:

- Typescript
- Babel
- Jest
npm package: @matthamlin/use-error
Description: A hook to trigger error boundaries higher up in the React tree.
Author: Matthew Hamlin
Latest version: 1.0.0

# `@matthamlin/use-error`

A hook to trigger error boundaries higher up in the React tree.

## API

```jsx
import { useError } from '@matthamlin/use-error'

function ComponentThatThrows() {
  let trigger = useError()

  useEffect(() => {
    if (thing) {
      // Calling trigger will force the parent error boundary
      trigger(`Passed 'thing' to ComponentThatThrows`)
    }
  })

  return <SomeComponent />
}
```

`useError` can be called with a single argument that is either:

- A string, this string will be passed to the `Error` constructor directly or
- Any other type which will be thrown
npm package: @matthamlin/use-local-storage
Description: A small hook wrapping the local storage API
Author: Matthew Hamlin
Latest version: 1.0.0

# `@matthamlin/use-local-storage`

A small hook wrapping the local storage API

![Coverage: 100%](https://img.shields.io/badge/coverage-100%25-brightgreen?logo=jest)

## API

The default export of the library is a `useLocalStorage` hook that you call with
another state-style hook. The requirement being that the value you provide
`useLocalStorage` should be an array with at least two values, the first being
the stateful value to store in local storage, and the second being an updater
function (e.g. dispatch, setState, etc.).

The second argument for the hook is an options object that supports the
following:

- `key` - A string that is used as the key for where the value will be stored in
  local storage
- `hydrate` - An optional boolean that if try will call setValue on the hook
  mounting with the value from localStorage
- `getItem` - An optional override to the default
  `JSON.parse(window.localStorage.getItem(key))`
- `setItem` - An optional override to the default
  `window.localStorage.setItem(key, JSON.stringify(value))`

```jsx
import useLocalStorage from '@matthamlin/use-local-storage'

function Component() {
  let [value, setValue] = useLocalStorage(useState('foo'), { key: 'your-app' })

  return (
    <label>
      Enter your name:
      <input
        type="text"
        value={value}
        onChange={({ target: { value } }) => setValue(value)}
      />
    </label>
  )
}
```
npm package: @matthamlin/use-refs
Description: A small package that offers a few shared functions to interact with React `ref`s
Author: Matthew Hamlin
Latest version: 1.0.0

# `@matthamlin/use-refs`

A small package that offers a few shared functions to interact with React `ref`s

![Coverage: 100%](https://img.shields.io/badge/coverage-100%25-brightgreen?logo=jest)

## API

```jsx
import { useRefs, useSharedRef, mergeRefs } from '@matthamlin/use-refs'

// useRefs
function Component() {
  // refs is a Map of key to element
  let [register, get, refs] = useRefs()

  useEffect(() => {
    // logs out the div element
    console.log(get('container'))
    // logs out the p element
    console.log(get('text'))
  })

  return (
    <div ref={register('container')}>
      <p ref={register('text')}>Text</p>
    </div>
  )
}

// useSharedRef
function AnotherComponent({ innerRef }) {
  let localRef = useSharedRef(innerRef)

  useEffect(() => {
    console.log(localRef.current)
  })

  return <div ref={localRef} />
}

// mergeRefs
function getElementProps({ ref, ...props }) {
  return {
    ref: mergeRefs(ref, localRef),
  }
}
```

### `useRefs`

This hook is a helper for working with multiple references within the same
component without needing to hardcode each `useRef` call.

It returns:

- `register` - a function that is called with a `key`, and then returns a
  function to accept the reference you want to store with that key
- `get` - a function for accessing a ref with a specific `key` - falls back to
  returning `null` if no `key` is found in the refs map
- `refs` - a `Map` instance mapping each `key` to the reference

### `useSharedRef`

This hook is a small helper hook for both applying a ref being passed in via
props as well as a local ref. This is useful for when both the parent and the
component need to store and read from a single ref.

It can be called with as many refs as you would like (either functions or ref
object).

It returns:

- `ref` - A `ref` object with a `current` property pointing to the reference

### `mergeRefs`

This is a helper function for applying multiple ref handlers to the same
reference. It is what implements the above `useSharedRef` helper.

It can be called with as many refs as you would like (either functions or ref
objects).

It returns:

- `handleRef` - A function that accepts a `reference` to be passed to each of
  the refs it was called with
npm package: @matthamlin/zaps
Description: > 🚨 This package is a work in progress, it may not work yet
Author: Matthew Hamlin
Latest version: 1.0.0

# `@matthamlin/zaps`

> 🚨 This package is a work in progress, it may not work yet

A monorepo toolchain.

## Architecture

### Definitions:

- Project - A collection of workspaces
- Workspace - A node package, identified by a `package.json` file

### Configuration

Workspaces are defined by a `workspaces` key in the project root package.json file (similar to yarn workspaces).

```json
{
  "workspaces": ["packages/*"]
}
```

## Commands:

- `zaps init`
  - Generates the zaps graph and zaps config file for the repo
- `zaps run`
  - Runs commands within a specified workspace
- `zaps exec`
  - Runs a command across all workspaces

## TODO:

- `zaps release`
  - Asks for a workspace
  - Runs build
  - Runs tests
  - Publishes
npm package: @matthesketh/ink-breadcrumb
Description: A navigation trail/breadcrumb component for Ink 5 terminal dashboards
Latest version: 0.1.0

# @matthesketh/ink-breadcrumb

A navigation trail/breadcrumb component for [Ink 5](https://github.com/vadimdemedes/ink). Shows the user's current position in a hierarchy.

## Install

```bash
npm install @matthesketh/ink-breadcrumb
```

## Usage

```tsx
import { Breadcrumb } from '@matthesketh/ink-breadcrumb';

<Breadcrumb path={['Dashboard', 'api-server', 'Logs']} />
```

Output: `Dashboard › api-server › **Logs**`

## Props

| Prop            | Type       | Default   | Description                        |
| --------------- | ---------- | --------- | ---------------------------------- |
| `path`          | `string[]` | required  | Breadcrumb segments                |
| `separator`     | `string`   | `' › '`   | Separator between segments         |
| `activeColor`   | `string`   | `'cyan'`  | Colour of the last (active) item   |
| `inactiveColor` | `string`   | `'gray'`  | Colour of parent items             |

## License

MIT
npm package: @matthesketh/ink-chart
Description: Terminal charts for Ink 5 — sparklines, bar charts, and line charts using Unicode block characters
Latest version: 0.1.0

# @matthesketh/ink-chart

Terminal charts for Ink 5 — sparklines, bar charts, and line charts using Unicode block characters.

## Install

```bash
npm install @matthesketh/ink-chart
```

## Components

### Sparkline

Single-row visualization using Unicode block characters (`▁▂▃▄▅▆▇█`).

```tsx
import { Sparkline } from '@matthesketh/ink-chart';

<Sparkline data={[1, 5, 2, 8, 3, 7]} color="green" />
```

| Prop    | Type       | Default         | Description                  |
|---------|------------|-----------------|------------------------------|
| data    | `number[]` | required        | Values to plot               |
| width   | `number`   | `data.length`   | Character width              |
| color   | `string`   | `'green'`       | Text color                   |
| min     | `number`   | auto            | Minimum scale value          |
| max     | `number`   | auto            | Maximum scale value          |

### BarChart

Vertical bar chart with labels and values.

```tsx
import { BarChart } from '@matthesketh/ink-chart';

<BarChart
  data={[
    { label: 'Mon', value: 12 },
    { label: 'Tue', value: 8, color: 'red' },
    { label: 'Wed', value: 15 },
  ]}
  height={10}
/>
```

| Prop       | Type                                              | Default | Description           |
|------------|---------------------------------------------------|---------|-----------------------|
| data       | `{ label: string; value: number; color?: string }[]` | required | Bar data           |
| height     | `number`                                          | `10`    | Chart height in rows  |
| width      | `number`                                          | auto    | Total width           |
| showValues | `boolean`                                         | `true`  | Show value above bars |

### LineChart

Multi-row line chart with optional axes.

```tsx
import { LineChart } from '@matthesketh/ink-chart';

<LineChart data={[1, 3, 2, 5, 4, 7, 6]} width={40} height={10} />
```

| Prop     | Type       | Default  | Description        |
|----------|------------|----------|--------------------|
| data     | `number[]` | required | Values to plot     |
| width    | `number`   | `40`     | Chart width        |
| height   | `number`   | `10`     | Chart height       |
| color    | `string`   | `'cyan'` | Line color         |
| showAxis | `boolean`  | `true`   | Show axes          |
| label    | `string`   | none     | Y-axis label       |

## License

MIT
npm package: @matthesketh/ink-checkbox
Description: A standalone inline checkbox toggle component for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-checkbox

A standalone inline checkbox toggle component for [Ink 5](https://github.com/vadimdemedes/ink). Unlike `ink-multi-select` which is a list, this is a single inline toggle control.

## Install

```
npm install @matthesketh/ink-checkbox
```

## Usage

```tsx
import React, { useState } from 'react';
import { render } from 'ink';
import { Checkbox } from '@matthesketh/ink-checkbox';

function App() {
  const [checked, setChecked] = useState(false);

  return (
    <Checkbox
      label="Enable notifications"
      checked={checked}
      onChange={setChecked}
      color="cyan"
    />
  );
}

render(<App />);
```

## Props

| Prop       | Type                        | Default   | Description                        |
| ---------- | --------------------------- | --------- | ---------------------------------- |
| `label`    | `string`                    | ---       | Text label for the checkbox        |
| `checked`  | `boolean`                   | ---       | Whether the checkbox is checked    |
| `onChange`  | `(checked: boolean) => void`| ---       | Called when toggled                 |
| `color`    | `string`                    | `'cyan'`  | Colour of the checked indicator    |
| `disabled` | `boolean`                   | `false`   | Dim display with no interaction    |

## Behaviour

- Renders a checked or unchecked ballot box indicator with the label
- Checked indicator shown in colour, unchecked in dim
- Label is bold when checked
- Disabled state shows everything in dim
- Purely presentational --- parent controls the checked state

## License

MIT
npm package: @matthesketh/ink-diff
Description: A diff viewer component for Ink 5 showing text differences with add/remove colouring
Latest version: 0.1.0

# @matthesketh/ink-diff

A diff viewer component for [Ink 5](https://github.com/vadimdemedes/ink) showing text differences with add/remove colouring. Supports unified and side-by-side (split) modes. Zero external dependencies -- implements a simple LCS-based diff algorithm inline.

## Install

```
npm install @matthesketh/ink-diff
```

Peer dependencies: `ink >= 5.0.0`, `react >= 18.0.0`

## Usage

```tsx
import React from 'react';
import { render } from 'ink';
import { DiffViewer } from '@matthesketh/ink-diff';

const oldText = `function greet() {
  return "hello";
}`;

const newText = `function greet() {
  return "hello, world";
}`;

render(
  <DiffViewer
    oldText={oldText}
    newText={newText}
    oldLabel="before"
    newLabel="after"
  />
);
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `oldText` | `string` | (required) | The original text |
| `newText` | `string` | (required) | The updated text |
| `mode` | `'unified' \| 'split'` | `'unified'` | Display mode |
| `context` | `number` | `3` | Lines of context around changes |
| `oldLabel` | `string` | - | Label for the old text (e.g. "before") |
| `newLabel` | `string` | - | Label for the new text (e.g. "after") |
| `maxHeight` | `number` | - | Maximum number of lines to display |

## Modes

### Unified

Shows all lines in a single column with `+` (green) for additions and `-` (red) for removals. Line numbers for both old and new files shown on the left.

### Split

Two columns side by side. Removed lines highlighted red on the left, added lines highlighted green on the right.

## Exports

- `DiffViewer` -- the React component
- `DiffViewerProps` -- TypeScript interface for props
- `diffLines(oldLines, newLines)` -- the underlying diff algorithm
- `applyContext(lines, context)` -- filter diff output to context lines
- `DiffLine` -- type for diff result entries

## License

MIT
npm package: @matthesketh/ink-file-picker
Description: Filesystem navigator and file picker for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-file-picker

A filesystem navigator and file picker for [Ink 5](https://github.com/vadimdemedes/ink). Navigate directories, select files, and filter by extension.

## Install

```bash
npm install @matthesketh/ink-file-picker
```

## Usage

```tsx
import React from 'react';
import { render } from 'ink';
import { FilePicker } from '@matthesketh/ink-file-picker';

function App() {
  return (
    <FilePicker
      extensions={['.ts', '.tsx']}
      onSelect={(filePath) => {
        console.log('Selected:', filePath);
      }}
      onCancel={() => {
        console.log('Cancelled');
      }}
    />
  );
}

render(<App />);
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `initialPath` | `string` | `process.cwd()` | Starting directory |
| `extensions` | `string[]` | `undefined` | Filter files by extension (e.g. `['.ts', '.tsx']`) |
| `showHidden` | `boolean` | `false` | Show hidden files (starting with `.`) |
| `maxVisible` | `number` | `15` | Maximum visible items in the windowed list |
| `onSelect` | `(path: string) => void` | required | Called when a file is selected |
| `onCancel` | `() => void` | `undefined` | Called when Escape is pressed |

## Keybindings

| Key | Action |
|-----|--------|
| `j` / `Down` | Move selection down |
| `k` / `Up` | Move selection up |
| `Enter` | Open directory or select file |
| `Backspace` | Go up one directory |
| `Escape` | Cancel |

## Features

- Folders listed first with `▸` prefix, files with `·` prefix
- File sizes shown in dim text
- Windowed scrolling with `maxVisible` limit and scroll indicators
- Extension filtering (directories always shown)
- Hidden file toggle

## License

MIT
npm package: @matthesketh/ink-form
Description: Form builder for Ink 5 with text inputs, selects, checkboxes, and switches
Latest version: 0.1.0

# @matthesketh/ink-form

A form builder for Ink 5 that composes text inputs, selects, checkboxes, and switches into a navigable form with validation.

## Install

```bash
npm install @matthesketh/ink-form
```

## Usage

```tsx
import { Form } from '@matthesketh/ink-form';

const fields = [
  { name: 'username', label: 'Username', type: 'text' as const, required: true },
  { name: 'password', label: 'Password', type: 'password' as const },
  { name: 'role', label: 'Role', type: 'select' as const, options: [
    { label: 'Admin', value: 'admin' },
    { label: 'User', value: 'user' },
  ]},
  { name: 'active', label: 'Active', type: 'boolean' as const, defaultValue: true },
];

function App() {
  return (
    <Form
      fields={fields}
      onSubmit={(values) => console.log(values)}
      onCancel={() => process.exit()}
    />
  );
}
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `fields` | `FormField[]` | required | Array of field definitions |
| `onSubmit` | `(values) => void` | required | Called with form values on submit |
| `onCancel` | `() => void` | - | Called when Escape is pressed |
| `submitLabel` | `string` | `'Submit'` | Label for the submit button |

## Field types

- **text** - inline text input
- **password** - masked input
- **select** - dropdown selection from options
- **boolean** - toggle switch

## Navigation

- Arrow keys / Tab to move between fields
- Enter to edit text fields or toggle booleans
- Enter on select fields opens a dropdown
- Enter on submit button submits the form
- Escape to cancel

## Licence

MIT
npm package: @matthesketh/ink-gauge
Description: A progress gauge and donut visualisation component for Ink 5 terminal dashboards
Latest version: 0.1.0

# @matthesketh/ink-gauge

A progress gauge and donut visualisation component for Ink 5 terminal dashboards.

## Install

```sh
npm install @matthesketh/ink-gauge
```

## Usage

### Gauge (horizontal bar)

```tsx
import { Gauge } from '@matthesketh/ink-gauge';

<Gauge value={75} />

<Gauge value={45} label="CPU" width={10} />

<Gauge
  value={90}
  thresholds={[
    { value: 80, color: 'yellow' },
    { value: 95, color: 'red' },
  ]}
/>
```

### Donut

```tsx
import { Donut } from '@matthesketh/ink-gauge';

<Donut value={40} />

<Donut value={75} size="large" />
```

## API

### `<Gauge />`

| Prop             | Type                                  | Default   | Description                          |
| ---------------- | ------------------------------------- | --------- | ------------------------------------ |
| `value`          | `number`                              | required  | 0-100 percentage                     |
| `width`          | `number`                              | `20`      | Bar character count                  |
| `filledChar`     | `string`                              | -         | Character for filled portion         |
| `emptyChar`      | `string`                              | -         | Character for empty portion          |
| `color`          | `string`                              | `'green'` | Colour of filled portion             |
| `showPercentage` | `boolean`                             | `true`    | Show percentage after bar            |
| `label`          | `string`                              | -         | Label before bar                     |
| `thresholds`     | `{ value: number; color: string }[]`  | -         | Auto-select colour based on value    |

### `<Donut />`

| Prop    | Type                   | Default   | Description                    |
| ------- | ---------------------- | --------- | ------------------------------ |
| `value` | `number`               | required  | 0-100 percentage               |
| `label` | `string`               | -         | Centre/end label               |
| `color` | `string`               | `'green'` | Colour                         |
| `size`  | `'small' \| 'large'`   | `'small'` | Small = 1 line, large = 3 line |

## License

MIT
npm package: @matthesketh/ink-input-dispatcher
Description: Single-point input routing for Ink 5 apps — no more competing useInput handlers
Latest version: 0.1.0

# @matthesketh/ink-input-dispatcher

Single-point input routing for Ink 5 apps. Eliminates the "multiple useInput handlers fighting" problem.

## The Problem

In Ink, every component that calls `useInput()` receives ALL keypresses. When your app has a global keyboard handler AND per-view handlers AND list components with their own handlers, inputs get processed multiple times causing flickering, double-actions, and character duplication.

## The Solution

One `useInput` call at the root. Views register their handlers, and only the active view's handler receives input.

## Install

```bash
npm install @matthesketh/ink-input-dispatcher
```

## Usage

```tsx
import { InputDispatcher, useRegisterHandler } from '@matthesketh/ink-input-dispatcher';
import type { InputHandler } from '@matthesketh/ink-input-dispatcher';

function App() {
  const globalHandler: InputHandler = (input, key) => {
    if (input === 'q') { process.exit(0); return true; }
    if (key.tab) { switchView(); return true; }
    return false;
  };

  return (
    <InputDispatcher globalHandler={globalHandler}>
      <ActiveView />
    </InputDispatcher>
  );
}

function ListView() {
  const handler: InputHandler = (input, key) => {
    if (input === 'j' || key.downArrow) { moveDown(); return true; }
    if (input === 'k' || key.upArrow) { moveUp(); return true; }
    return false;
  };

  useRegisterHandler(handler);
  return <ScrollableList ... />;
}
```

## API

### `<InputDispatcher globalHandler? children />`

Wraps your app. Owns the single `useInput` call.

- `globalHandler`: Called first for every keypress. Return `true` to consume.

### `useRegisterHandler(handler)`

Registers the calling component as the active input handler. Only one handler active at a time. Cleans up on unmount.

## Requirements

- Ink >= 5.0.0
- React >= 18.0.0
npm package: @matthesketh/ink-keybinding-help
Description: Auto-formatted keybinding reference overlay for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-keybinding-help

Auto-formatted keybinding reference overlay for Ink 5. Renders a bordered help panel from a config object, similar to pressing `?` in vim or htop.

## Install

```bash
npm install @matthesketh/ink-keybinding-help
```

## Usage

```tsx
import React, { useState } from 'react';
import { render, useInput } from 'ink';
import { KeyBindingHelp } from '@matthesketh/ink-keybinding-help';

function App() {
  const [showHelp, setShowHelp] = useState(false);

  useInput((input) => {
    if (input === '?') setShowHelp((v) => !v);
  });

  return (
    <KeyBindingHelp
      visible={showHelp}
      title="Keyboard Shortcuts"
      columns={2}
      groups={[
        {
          title: 'Navigation',
          bindings: [
            { key: 'j/k', description: 'navigate list' },
            { key: 'Tab', description: 'switch view' },
          ],
        },
        {
          title: 'Actions',
          bindings: [
            { key: 'Enter', description: 'confirm selection' },
            { key: 'q', description: 'quit' },
          ],
        },
      ]}
    />
  );
}

render(<App />);
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `groups` | `KeyBindingGroup[]` | *required* | Array of keybinding groups to display |
| `title` | `string` | `"Keyboard Shortcuts"` | Title shown at the top of the panel |
| `columns` | `number` | `2` | Number of columns to arrange groups in |
| `visible` | `boolean` | `true` | Whether the panel is rendered; set to `false` to hide |

### KeyBindingGroup

| Field | Type | Description |
|-------|------|-------------|
| `title` | `string` | Section header (e.g. "Navigation") |
| `bindings` | `KeyBinding[]` | List of key-description pairs |

### KeyBinding

| Field | Type | Description |
|-------|------|-------------|
| `key` | `string` | Key or key combination (e.g. "j/k", "Ctrl+c") |
| `description` | `string` | What the key does |

## Requirements

- Node.js >= 18
- `ink` >= 5.0.0
- `react` >= 18.0.0
npm package: @matthesketh/ink-log-viewer
Description: Rolling log display component for Ink 5 with auto-scroll, filtering, and timestamps
Latest version: 0.1.0

# @matthesketh/ink-log-viewer

A rolling log display component for [Ink 5](https://github.com/vadimdemedes/ink) with auto-scroll, filtering, and timestamps. Like blessed-contrib's rolling log but for Ink.

## Install

```sh
npm install @matthesketh/ink-log-viewer
```

## Usage

```tsx
import React, { useState } from 'react';
import { render } from 'ink';
import { LogViewer, type LogLine } from '@matthesketh/ink-log-viewer';

function App() {
  const [lines] = useState<LogLine[]>([
    { text: 'Server started', level: 'info', timestamp: new Date() },
    { text: 'Listening on :3000', level: 'info', timestamp: new Date() },
    { text: 'Connection timeout', level: 'warn', timestamp: new Date() },
  ]);

  return (
    <LogViewer
      lines={lines}
      height={10}
      showTimestamps
      showLevel
    />
  );
}

render(<App />);
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `lines` | `LogLine[]` | required | Array of log lines to display |
| `height` | `number` | required | Number of visible rows |
| `showTimestamps` | `boolean` | `false` | Show `HH:MM:SS` timestamp prefix (dimmed) |
| `showLevel` | `boolean` | `false` | Show coloured level badge |
| `filter` | `string` | — | Case-insensitive substring filter |
| `autoScroll` | `boolean` | `true` | Pin view to newest lines |
| `wrap` | `boolean` | `true` | Wrap long lines (false = truncate) |

## LogLine

```ts
interface LogLine {
  text: string;
  timestamp?: Date;
  level?: 'info' | 'warn' | 'error' | 'debug';
}
```

## Level badges

- **INFO** — blue
- **WARN** — yellow
- **ERR** — red
- **DBG** — dimmed

## License

MIT
npm package: @matthesketh/ink-markdown
Description: Render markdown in the terminal for Ink 5 — headings, bold, italic, code blocks, lists, links, blockquotes, and horizontal rules
Latest version: 0.1.0

# @matthesketh/ink-markdown

Render markdown in the terminal for Ink 5.

Handles headings, bold, italic, code blocks, inline code, lists (bullet and ordered), links, blockquotes, and horizontal rules. No external markdown parser dependency.

## Install

```
npm install @matthesketh/ink-markdown
```

## Usage

```tsx
import { Markdown } from '@matthesketh/ink-markdown';

function App() {
  const text = `
# Hello World

This is **bold** and *italic* text.

- bullet one
- bullet two

> a blockquote
`;

  return <Markdown maxWidth={80}>{text}</Markdown>;
}
```

## Props

| Prop       | Type     | Description              |
| ---------- | -------- | ------------------------ |
| `children` | `string` | Markdown text to render  |
| `maxWidth` | `number` | Optional wrap width      |

## Supported markdown

- `# Heading 1` - bold + uppercase
- `## Heading 2` - bold
- `### Heading 3` - bold + dim
- `**bold**` - bold text
- `*italic*` / `_italic_` - dim text
- `` `inline code` `` - inverse/highlighted
- Code blocks (triple backtick) - indented, dim
- `- item` / `* item` - bullet lists
- `1. item` - numbered lists
- `---` / `***` - horizontal rule
- `[text](url)` - underlined text with dimmed url
- `> blockquote` - indented with bar prefix

## Licence

MIT
npm package: @matthesketh/ink-masked-input
Description: A template-formatted input component for Ink 5. Handles IP addresses, dates, phone numbers, and custom mask patterns.
Latest version: 0.1.0

# @matthesketh/ink-masked-input

A template-formatted input component for Ink 5. Handles IP addresses, dates, phone numbers, and custom mask patterns.

## Install

```bash
npm install @matthesketh/ink-masked-input
```

## Usage

```tsx
import { MaskedInput, MASKS } from '@matthesketh/ink-masked-input';

function IpInput() {
  const [value, setValue] = useState('');
  return (
    <MaskedInput
      mask={MASKS.ip}
      value={value}
      onChange={setValue}
      onSubmit={(v) => console.log('IP:', v)}
    />
  );
}
```

## Mask characters

| Character | Matches         |
|-----------|-----------------|
| `9`       | Any digit (0-9) |
| `a`       | Any letter (a-z, A-Z) |
| `*`       | Any character   |
| Other     | Literal (rendered as-is, cursor skips over) |

## Presets

| Name    | Mask                    |
|---------|-------------------------|
| `ip`    | `999.999.999.999`       |
| `date`  | `99/99/9999`            |
| `time`  | `99:99`                 |
| `phone` | `+99 (999) 999-9999`   |
| `mac`   | `**:**:**:**:**:**`     |

## Props

| Prop        | Type                        | Default | Description |
|-------------|-----------------------------|---------|-------------|
| mask        | `string`                    | —       | Mask pattern |
| value       | `string`                    | —       | Current raw value (editable chars only) |
| onChange    | `(value: string) => void`   | —       | Called when value changes |
| onSubmit   | `(value: string) => void`   | —       | Called on Enter when all positions filled |
| placeholder | `string`                    | mask    | Placeholder display |
| focus       | `boolean`                   | `true`  | Whether input is active |

## License

MIT
npm package: @matthesketh/ink-modal
Description: Overlay dialog component for Ink 5 apps
Latest version: 0.1.0

# @matthesketh/ink-modal

An overlay dialog component for [Ink 5](https://github.com/vadimdemedes/ink). Since Ink's Yoga layout doesn't support true z-index, this renders a bordered box that visually appears as an overlay by being placed in the component tree above other content.

## Install

```sh
npm install @matthesketh/ink-modal
```

## Usage

```tsx
import React from 'react';
import { render, Text } from 'ink';
import { Modal } from '@matthesketh/ink-modal';

function App() {
  const [open, setOpen] = React.useState(true);

  return (
    <>
      <Text>Background content</Text>
      <Modal
        visible={open}
        title="Confirm Action"
        footer="[Enter] Confirm  [Esc] Cancel"
      >
        <Text>Are you sure you want to proceed?</Text>
      </Modal>
    </>
  );
}

render(<App />);
```

## Props

| Prop          | Type              | Default   | Description                          |
| ------------- | ----------------- | --------- | ------------------------------------ |
| `visible`     | `boolean`         | -         | Whether the modal is shown           |
| `title`       | `string`          | -         | Bold title at the top of the modal   |
| `width`       | `number`          | `50`      | Fixed width of the modal box         |
| `borderColor` | `string`          | `'cyan'`  | Color of the rounded border          |
| `children`    | `React.ReactNode` | -         | Body content                         |
| `footer`      | `React.ReactNode` | -         | Dimmed footer text at the bottom     |

## License

MIT
npm package: @matthesketh/ink-pager
Description: A less-like scrollable content viewer for Ink 5
Latest version: 0.1.0

# @matthesketh/ink-pager

A less-like scrollable content viewer for Ink 5. Displays multi-line text content with keyboard scrolling, line numbers, and search.

## Install

```bash
npm install @matthesketh/ink-pager
```

## Usage

```tsx
import { Pager } from '@matthesketh/ink-pager';

function App() {
  const [offset, setOffset] = useState(0);

  return (
    <Pager
      content={logOutput}
      height={20}
      showLineNumbers
      searchQuery="error"
      scrollOffset={offset}
      onScroll={setOffset}
    />
  );
}
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `content` | `string` | required | The text to display |
| `height` | `number` | required | Visible rows |
| `showLineNumbers` | `boolean` | `false` | Show line numbers on the left |
| `wrap` | `boolean` | `true` | Word wrap long lines |
| `searchQuery` | `string` | - | Highlight matches in yellow/bold |
| `scrollOffset` | `number` | `0` | Controlled scroll position |
| `onScroll` | `(offset: number) => void` | - | Scroll position callback |

## Behaviour

- Splits content into lines, renders a window of `height` lines starting from `scrollOffset`
- Line numbers shown in dim colour on the left when enabled
- Word wrap splits long lines to fit terminal width
- Search query highlights matches in yellow/bold
- Shows scroll position indicator: `Line X-Y of Z` at bottom
- Presentational component only; parent controls `scrollOffset`