# Mask Token Input

Mask Token Input [#mask-token-input]

The **Mask Token Input** is a textarea for fill-mask / cloze input. It detects a configurable mask token (default `[MASK]`), renders an inline highlighted preview with the mask span accented, shows a validation hint (`Mask detected` vs `Add [MASK]`), offers a randomize button that inserts a valid sample, and surfaces a Cmd+Enter badge for one-key submit.

It is presentational and portable — pair its `value` / `onSubmit` with any fill-mask backend to run predictions and render the returned fills. With LocalMode you can (optionally) wire it to [`useFillMask()`](https://localmode.dev/docs/react) from `@localmode/react`.

**When to use it:** any masked-language task — "The capital of France is `[MASK]`." → top predictions.

Preview [#preview]

```tsx
'use client';

import { useState } from 'react';
import { useFillMask } from '@localmode/react';
import { transformers } from '@localmode/transformers';
import { MaskTokenInput } from '@/components/mask-token-input';

/**
 * Demo for MaskTokenInput, used by the docs live preview. Wires the input to a
 * real `useFillMask` flow backed by a small BERT model. The model downloads on
 * the first submit (Run-gated), then predictions render below.
 */
export default function MaskTokenInputDemo() {
  const [text, setText] = useState('The capital of France is [MASK].');
  const { data, isLoading, error, execute } = useFillMask({
    model: transformers.fillMask('Xenova/bert-base-uncased'),
    topK: 5,
  });

  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      <MaskTokenInput
        value={text}
        onChange={setText}
        onSubmit={execute}
        disabled={isLoading}
      />

      {isLoading && (
        <p className="text-sm text-muted-foreground">Predicting…</p>
      )}
      {error && (
        <p className="text-sm text-destructive">{error.message}</p>
      )}
      {data && !isLoading && (
        <ul className="flex flex-col gap-1 text-sm">
          {data.predictions.map((p) => (
            <li
              key={p.token}
              className="flex items-center justify-between rounded-md border border-border px-3 py-1.5"
            >
              <span className="font-medium">{p.token}</span>
              <span className="font-mono text-xs text-muted-foreground">
                {(p.score * 100).toFixed(1)}%
              </span>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/input-controls/mask-token-input
```

Dependencies [#dependencies]

* **Data source:** renders the `value` you pass and emits `onChange` / `onSubmit` callbacks — works with any backend. Recommended LocalMode producer: `useFillMask` (optional).
* No npm dependencies (only the shared `cn()` util via `@localmode/ui/lib/utils`).

Files installed [#files-installed]

* `mask-token-input.tsx` — the component
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**MaskTokenInput**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` | — | **Required.** Current text value (controlled). |
| `onChange` | `function` | — | **Required.** Fired with the new text on every edit. |
| `maskToken` | `string` | `"[MASK]"` | The mask token the fill-mask model expects. |
| `onSubmit` | `function` | — | Fired on Cmd/Ctrl+Enter (and via the inline hint button) when the value contains the mask token — wire this to your `useFillMask().execute(value)`. |
| `samples` | `array` | — | Sample sentences inserted by the randomize button. Each should contain the `maskToken`. If omitted, a small built-in set is used. |
| `placeholder` | `string` | — | Placeholder for the textarea. |
| `ariaLabel` | `string` | `"Fill-mask sentence"` | Accessible name for the textarea. The field has no associated visible `<label>`, so this is applied as its `aria-label`; override it to match a nearby heading in your layout. |
| `disabled` | `boolean` | — | Disable input and actions (e.g. while a prediction is running). |

Examples [#examples]

Wired to `useFillMask` [#wired-to-usefillmask]

```tsx
import { useState } from 'react';
import { useFillMask } from '@localmode/react';
import { transformers } from '@localmode/transformers';
import { MaskTokenInput } from '@/components/mask-token-input';

export function ClozeBox() {
  const [text, setText] = useState('The capital of France is [MASK].');
  const { data, isLoading, execute } = useFillMask({
    model: transformers.fillMask('Xenova/bert-base-uncased'),
    topK: 5,
  });

  return (
    <div className="flex flex-col gap-3">
      <MaskTokenInput value={text} onChange={setText} onSubmit={execute} disabled={isLoading} />
      {data?.predictions.map((p) => (
        <div key={p.token}>{p.token} — {(p.score * 100).toFixed(1)}%</div>
      ))}
    </div>
  );
}
```

Custom mask token [#custom-mask-token]

```tsx
<MaskTokenInput value={text} onChange={setText} maskToken="<mask>" />
```

Accessible name [#accessible-name]

The textarea has no visible `<label>`, so it ships with an `aria-label` (default `"Fill-mask sentence"`). Override `ariaLabel` to match a nearby heading in your layout.

```tsx
<MaskTokenInput value={text} onChange={setText} ariaLabel="Sentence to complete" />
```

Customization [#customization]

The mask span is highlighted with `bg-primary/15 text-primary`; the validation hint uses Tailwind's `emerald` / `amber` palettes. Pass your own `samples` (each containing the mask token) to control what Randomize inserts. Set `ariaLabel` to give the textarea a programmatic name. The component is theme-driven via shadcn/ui CSS variables — edit the copied `mask-token-input.tsx` to change the preview styling or add multi-mask support.