# API Keys (Lumen Halo): prompt.md (v1.0.0)

- id: `lumen-api-keys` · version 1.0.0 · block · pro (Pro)
- category: App screens
- build: Base UI (one set of files; its dependencies follow the build)
- install (this build): `npx shadcn@latest add @beautiful-ui-pro/lumen-api-keys`
- npm dependencies: none
- registry dependencies: utils, @beautiful-ui/lumen-button, @beautiful-ui/lumen-field, @beautiful-ui/lumen-input, @beautiful-ui/lumen-native-select, @beautiful-ui/lumen-clipboard-button, @beautiful-ui-pro/lumen-safe-action, https://beautiful-ui.dev/r/lumen-foundation.json
- docs: https://beautiful-ui.dev/components/lumen-api-keys
- The install command carries everything this item needs (files, CSS, tokens, npm and registry dependencies). Prefer it to copying source by hand.

A workspace's API keys, and making a new one. Create key unfolds a short form in place; the new secret is shown exactly once with a copy button and a plain warning, then only its last four characters remain. Revoking asks for a second press in place.

## Build it
- Stack: React 19 (`ref` is a plain prop), TypeScript, Tailwind CSS v4 utilities, a shadcn-initialised project with the `@/*` alias.
- Packages: none beyond React.
- Files: `components/ui/lumen/blocks/api-keys.tsx`.
- Registry dependencies, installed with it automatically: shadcn `utils` (cn), `lumen-button`, `lumen-field`, `lumen-input`, `lumen-native-select`, `lumen-clipboard-button`, `lumen-safe-action`, `lumen-foundation`.
- Builds: one set of files for both, but its dependencies come in Base UI and Radix builds. Install the one that matches the project (see Install): a free item's bare URL installs the Base UI build of it and its dependencies.
- Exports to keep: `ApiKeys`, and every exported type.
- CSS: the install merges this item's rules (the registry `css` field) into your global stylesheet, in `@layer components`, and adds the lumen foundation (tokens, keyframes, motion levels) once. Nothing to import by hand.
- Re-running `add` (or `--overwrite`) re-applies those rules: put overrides in your own CSS, never in the installed rules.
- Tokens: retheme with the `--lumen-*` custom properties (`--lumen-accent`, `--lumen-accent-foreground`, `--lumen-accent-text`, `--lumen-bad`, `--lumen-bad-text`, `--lumen-chart-accent`, `--lumen-chart-accent-foreground`, `--lumen-chart-accent-text`, `--lumen-focus`, `--lumen-good`, `--lumen-good-text`, `--lumen-hairline`, `--lumen-ink`, `--lumen-muted-ink`, `--lumen-series-1`, `--lumen-series-2`, `--lumen-series-3`, `--lumen-series-4`, `--lumen-series-5`, `--lumen-series-6`, `--lumen-warn`, `--lumen-warn-text`); this item's CSS also reads `--lumen-font-mono`, `--lumen-font-sans`, `--lumen-radius-k`. Never add Tailwind colour classes inside the component.

```tsx
import { ApiKeys, type ApiKey } from "@/components/ui/lumen/blocks/api-keys";
```

## Parts

| Part | data-slot | What it is for |
|---|---|---|
| `ApiKeys` | `api-keys` | The header, the create tray (form, then the one-time secret) and the key list. |

Style a part with `[data-slot="<slot>"]` selectors or its `className`; keep the attributes when editing.

## Sound
- Keep every `data-slot` and `data-sound` attribute: the sound layer reads them.
- Installing this item adds no audio. Nothing plays until the app mounts `GlassSoundProvider` once (install: `npx shadcn@latest add https://beautiful-ui.dev/r/glass-sound.json`, import from `@/components/beautiful-ui/glass-sound`); `GlassSoundToggle` is its mute control. Without a provider the audio engine never loads.

## Match the original
- Read `components/ui/lumen/blocks/api-keys.tsx` as the reference implementation before changing or recreating anything, and match it: sizes, colours per theme, motion timings, copy and behaviour.
- If you deviate (a prop you can't honour, a style you changed, a dependency you swapped), say so in your reply, part by part.
- Keep the accessibility contract, the keyboard map and the motion levels listed below.

## Use it when
- api keys, developer settings, access tokens, personal access tokens, create key, revoke key, secret shown once
- A developer settings page where people create, see and revoke API keys or tokens

### Not when
- Showing one value to copy: use Clipboard Button
- Passwords: use Password Field

## Mistakes
- Never store or pass the secret in keys; only last4 and an optional prefix
- Add the new key to keys yourself inside onCreate (the component shows the secret, your list shows the row)
- Hash keys on the server; the secret should exist only in the create response

## Usage

```tsx
"use client";
import * as React from "react";
import { ApiKeys, type ApiKey } from "@/components/ui/lumen/blocks/api-keys";

export function DeveloperSettings({ initial }: { initial: ApiKey[] }) {
  const [keys, setKeys] = React.useState(initial);
  return (
    <ApiKeys
      keys={keys}
      onCreate={async (input) => {
        const res = await fetch("/api/keys", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(input) });
        if (!res.ok) throw new Error("Couldn't create the key. Try again.");
        const made: { key: ApiKey; secret: string } = await res.json();
        setKeys((k) => [made.key, ...k]);
        return made;
      }}
      onRevoke={async (key) => {
        const res = await fetch(`/api/keys/${key.id}`, { method: "DELETE" });
        if (!res.ok) throw new Error("Couldn't revoke it");
        setKeys((k) => k.filter((x) => x.id !== key.id));
      }}
    />
  );
}
```

## Props

| Prop | Type | Default | What it does |
|---|---|---|---|
| `keys` | `ApiKey[]` |  | ApiKey = { id, name, last4, prefix?, scope?, created, lastUsed?, expires? }. Dates come already worded. Never pass secrets here. |
| `onCreate` | `(input: { name, scope, expiry }) => Promise<{ key, secret }>` |  | Make the key on your server; resolve with the new key and its secret (shown once). Throw to show an error in the form. |
| `onRevoke` | `(key) => unknown` |  | Revoke it. Return a promise to show progress; throw to show the error. |
| `scopes / expiries` | `{ value, label }[] / { value, label }[]` |  | The access and expiry choices. Defaults: read only, read and write; 30 days, 90 days, a year, never. |
| `labels` | `Partial<ApiKeysLabels>` |  | Every word, for i18n. |

Full docs: https://beautiful-ui.dev/components/lumen-api-keys

## Customising
- Colours: set the `--lumen-*` tokens on `:root`, or on a container with the `lumen-scope` class to retheme one area. Add the `lumen-inherit` class to follow your shadcn palette instead (`--chart-N`, `--destructive`).
- Dark mode follows the `.dark` class on an ancestor (the shadcn and next-themes convention).
- Update later by re-running the install with `--overwrite` (review the diff if you edited it). Changelog: https://beautiful-ui.dev/r/changelog.json

## Keyboard

| Keys | Action |
|---|---|
| Enter (Create key) | Open the form; focus lands on Name |
| Enter (in the form) | Create the key |
| Tab | Through the fields, Cancel and Create |
| Enter / Space (Revoke) | Arm it; focus moves to Cancel, Tab to the confirm button and press it |

## Performance

- Plain rows; fine for the few dozen keys a workspace has

## Responsive

- Under 620px of width the dates move below each key; under 560px the form's fields stack

## Motion inventory

| Interaction | What moves |
|---|---|
| Create | The tray unfolds in place (no layout jump below it) |
| New key | Its row lights faintly as it arrives |
| Revoke | Safe Action's confirm morph |

## Accessibility contract (preserve when editing)
- A labelled section; the form's fields have visible labels and a missing name is said inline (role alert) and tied to the field
- Focus moves into the form when it opens, to Done when the secret shows, and back to Create key when it closes
- Keys read as "ending in a91f"; the dots are hidden from screen readers
- Revoke is a two-step press with the key's name in its label

## Install

```bash
npx shadcn@latest add @beautiful-ui-pro/lumen-api-keys
```

Pro item: needs the `@beautiful-ui-pro` registry in `components.json` and `BEAUTIFUL_UI_TOKEN` in `.env.local` (https://beautiful-ui.dev/account). Setup: https://beautiful-ui.dev/docs/pro. Your components.json `style` picks the build: radix-*, new-york and default get Radix, base-* gets Base UI.

The lumen foundation (the tokens listed above, keyframes and motion levels) installs once with the first component; its CSS is public at https://beautiful-ui.dev/r/lumen-foundation.json.
