https://github.com/extedcoud/smart-components
Headless React components powered by LLM behaviors.
https://github.com/extedcoud/smart-components
ai anthropic autocomplete copilot headless-ui llm openai react react-hooks typescript ui-components
Last synced: 27 days ago
JSON representation
Headless React components powered by LLM behaviors.
- Host: GitHub
- URL: https://github.com/extedcoud/smart-components
- Owner: extedcouD
- License: mit
- Created: 2026-05-12T06:21:44.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-21T19:17:07.000Z (29 days ago)
- Last Synced: 2026-05-22T04:06:12.494Z (28 days ago)
- Topics: ai, anthropic, autocomplete, copilot, headless-ui, llm, openai, react, react-hooks, typescript, ui-components
- Language: TypeScript
- Homepage: https://extedcoud.github.io/smart-components/
- Size: 418 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
Awesome Lists containing this project
README
# @extedcoud/smart-components
[](https://www.npmjs.com/package/@extedcoud/smart-components)
[](https://bundlephobia.com/package/@extedcoud/smart-components)
[](./LICENSE)
Headless React components with the AI wiring already done. You bring an LLM client and your own styles; the library handles the fiddly parts — debouncing, aborting stale requests, ghost-text positioning, keyboard handling.
### 📖 [Documentation & live demos](https://extedcoud.github.io/smart-components/)
### 🎮 [Component playground (Storybook)](https://extedcoud.github.io/smart-components/storybook/)
## Install
```sh
pnpm add @extedcoud/smart-components react react-dom
```
No runtime dependencies beyond React. Every adapter is built on plain `fetch`.
## Quick start
```tsx
import { SmartProvider, SmartTextbox, SmartSuggestion } from '@extedcoud/smart-components';
import { createProxyClient } from '@extedcoud/smart-components/adapters/proxy';
import '@extedcoud/smart-components/style.css';
// In production, point this at your own backend so the API key stays server-side.
const client = createProxyClient({ url: '/api/smart' });
export function App() {
const [v, setV] = useState('');
return (
console.log('picked', s)}
context="naming a new project"
/>
);
}
```
## Components
| Component | What it does |
| --- | --- |
| `` | Single-line input with Copilot-style ghost completion. ArrowRight accepts (configurable), Esc dismisses. |
| `` | Multiline ghost completion via a mirror div, with multiline stops and optional auto-resize. |
| `` | Combobox with an AI-generated dropdown. Arrow keys to move, Enter to pick. |
| `` | Render-prop rewrite primitive. Ships with Shorter / Formal / Casual / Fix grammar presets. |
Everything is headless: minimal default DOM, render slots (`renderItem`, `renderGhost`, render-props), and native input attributes pass straight through.
### Styling the ghost text
The ghost defaults to `opacity: 0.4` and inherits the input's color. To recolor it, pass `ghostClassName` or `ghostStyle`:
```tsx
```
For richer markup (icons, badges) use the `renderGhost` render-prop. To style globally, target `[data-testid="smart-textbox-ghost"]` / `[data-testid="smart-textarea-ghost"]`.
## Mobile
Touch is a first-class target, not an afterthought. Things to know:
- **The accept key is configurable.** It defaults to `ArrowRight`, which most soft keyboards lack — on mobile, remap `acceptKey` or trigger accept imperatively via the field ref.
- Wrappers use `touch-action: manipulation` to drop the 300ms tap delay.
- `SmartSuggestion` selection runs on pointer events (mouse, touch, pen). Give items `min-height: 44px` to hit the WCAG touch target.
- Inputs below `16px` font size make iOS Safari zoom on focus — keep `SmartTextbox` / `SmartTextarea` at 16px or larger.
- Known limit: the `SmartSuggestion` dropdown can sit under the soft keyboard on short viewports. Portal the list or use a fullscreen picker there.
## AI clients
`SmartClient` is a capability-based interface. Use an adapter or implement your own:
| Adapter | Import | When |
| --- | --- | --- |
| `createProxyClient` | `/adapters/proxy` | Production. POSTs to your backend; the key stays on the server. |
| `createOpenAIClient` | `/adapters/openai` | Dev and demos only — never ship a key to the browser. |
| `createAnthropicClient` | `/adapters/anthropic` | Anthropic Messages API (`complete` + `stream`). |
| `createMockClient` | `/adapters/mock` | Tests and Storybook. |
Rolling your own is a few lines:
```ts
import { SMART_CLIENT_PROTOCOL_VERSION, type SmartClient } from '@extedcoud/smart-components';
const myClient: SmartClient = {
protocolVersion: SMART_CLIENT_PROTOCOL_VERSION,
id: 'my-backend',
capabilities: new Set(['complete', 'stream']),
async complete(req) { /* ... */ return text; },
async *stream(req) { /* yield chunks */ },
};
```
## Hooks
The components are thin wrappers over hooks. Reach for these to build your own on the same plumbing:
```ts
import {
useGhostCompletion,
useSuggestionList,
useRewrite,
useSmartState,
} from '@extedcoud/smart-components';
```
### `useSmartState` — useState that an LLM can fill
A drop-in `useState` with one extra: `ai.generate(context?)`. The shape is read from your initial value, so the model is constrained to matching JSON — no schema, no parsing.
```tsx
const [user, setUser, ai] = useSmartState(
{ name: '', age: 0, bio: '' },
'a fictitious cyberpunk character',
);
ai.generate(); // → { name: 'Kael-9', age: 28, bio: '…' }
setUser({ ... }); // still plain useState
```
When the seed is empty (`null`, `[]`), pass `options.shape` so it knows what to ask for:
```tsx
const [tags, , ai] = useSmartState([], 'tags for a sci-fi blog post', {
shape: { type: 'array', item: 'string' },
});
```
A few things worth knowing:
- Calling `setValue` mid-generate cancels the in-flight request — the user's edit wins.
- An empty inner array (`{ tags: [] }`) throws at mount; seed it or pass `shape`. Same for `Date`, `Map`, `Set`, functions — JSON-serializable values only.
- Results are cached LRU(16) by `(shape, context)`. Opt out with `{ cache: false }`.
- Generate is atomic (no streaming) and needs a client with the `complete` capability.
Full walkthrough — form autofill, text extraction, palettes — is in the [docs](https://extedcoud.github.io/smart-components/).
## Develop
A pnpm workspace: the root is the library, `docs/` is the public site (Next.js + Fumadocs on GitHub Pages).
```sh
pnpm install
pnpm storybook # :6006 — internal dev surface
pnpm test # vitest watch
pnpm lint
pnpm typecheck
pnpm build # lib → dist/
# Docs site (build the lib first — it consumes dist/)
pnpm --filter @extedcoud/smart-components build
pnpm --filter docs dev # :3000
```
Docs deploy to GitHub Pages via `.github/workflows/deploy-docs.yml` on push to `main`.
## Stack
**Library:** Vite (lib mode), TypeScript, CSS Modules, ESLint 9, Prettier, Vitest + RTL, Storybook.
**Docs:** Next.js 15 (static export), Fumadocs UI, Tailwind v4, MDX.