{"id":50126378,"url":"https://github.com/extedcoud/smart-components","last_synced_at":"2026-05-23T20:04:31.355Z","repository":{"id":357311307,"uuid":"1236314869","full_name":"extedcouD/smart-components","owner":"extedcouD","description":"Headless React components powered by LLM behaviors.","archived":false,"fork":false,"pushed_at":"2026-05-21T19:17:07.000Z","size":428,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T04:06:12.494Z","etag":null,"topics":["ai","anthropic","autocomplete","copilot","headless-ui","llm","openai","react","react-hooks","typescript","ui-components"],"latest_commit_sha":null,"homepage":"https://extedcoud.github.io/smart-components/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/extedcouD.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-12T06:21:44.000Z","updated_at":"2026-05-21T19:17:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/extedcouD/smart-components","commit_stats":null,"previous_names":["extedcoud/smart-components"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/extedcouD/smart-components","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extedcouD%2Fsmart-components","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extedcouD%2Fsmart-components/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extedcouD%2Fsmart-components/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extedcouD%2Fsmart-components/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/extedcouD","download_url":"https://codeload.github.com/extedcouD/smart-components/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extedcouD%2Fsmart-components/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33410379,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T18:09:33.147Z","status":"ssl_error","status_checked_at":"2026-05-23T18:09:31.380Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","anthropic","autocomplete","copilot","headless-ui","llm","openai","react","react-hooks","typescript","ui-components"],"created_at":"2026-05-23T20:04:00.426Z","updated_at":"2026-05-23T20:04:31.348Z","avatar_url":"https://github.com/extedcouD.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @extedcoud/smart-components\n\n[![npm](https://img.shields.io/npm/v/@extedcoud/smart-components?color=cb3837\u0026logo=npm)](https://www.npmjs.com/package/@extedcoud/smart-components)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/@extedcoud/smart-components?label=min%2Bgzip)](https://bundlephobia.com/package/@extedcoud/smart-components)\n[![license](https://img.shields.io/npm/l/@extedcoud/smart-components?color=blue)](./LICENSE)\n\nHeadless 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.\n\n### 📖 [Documentation \u0026amp; live demos](https://extedcoud.github.io/smart-components/)\n\n### 🎮 [Component playground (Storybook)](https://extedcoud.github.io/smart-components/storybook/)\n\n## Install\n\n```sh\npnpm add @extedcoud/smart-components react react-dom\n```\n\nNo runtime dependencies beyond React. Every adapter is built on plain `fetch`.\n\n## Quick start\n\n```tsx\nimport { SmartProvider, SmartTextbox, SmartSuggestion } from '@extedcoud/smart-components';\nimport { createProxyClient } from '@extedcoud/smart-components/adapters/proxy';\nimport '@extedcoud/smart-components/style.css';\n\n// In production, point this at your own backend so the API key stays server-side.\nconst client = createProxyClient({ url: '/api/smart' });\n\nexport function App() {\n  const [v, setV] = useState('');\n  return (\n    \u003cSmartProvider client={client} model=\"gpt-4o-mini\"\u003e\n      \u003cSmartTextbox value={v} onChange={setV} context=\"user is writing a support reply\" /\u003e\n      \u003cSmartSuggestion\n        value={v}\n        onChange={setV}\n        onSelect={(s) =\u003e console.log('picked', s)}\n        context=\"naming a new project\"\n      /\u003e\n    \u003c/SmartProvider\u003e\n  );\n}\n```\n\n## Components\n\n| Component | What it does |\n| --- | --- |\n| `\u003cSmartTextbox\u003e` | Single-line input with Copilot-style ghost completion. ArrowRight accepts (configurable), Esc dismisses. |\n| `\u003cSmartTextarea\u003e` | Multiline ghost completion via a mirror div, with multiline stops and optional auto-resize. |\n| `\u003cSmartSuggestion\u003e` | Combobox with an AI-generated dropdown. Arrow keys to move, Enter to pick. |\n| `\u003cSmartRewrite\u003e` | Render-prop rewrite primitive. Ships with Shorter / Formal / Casual / Fix grammar presets. |\n\nEverything is headless: minimal default DOM, render slots (`renderItem`, `renderGhost`, render-props), and native input attributes pass straight through.\n\n### Styling the ghost text\n\nThe ghost defaults to `opacity: 0.4` and inherits the input's color. To recolor it, pass `ghostClassName` or `ghostStyle`:\n\n```tsx\n\u003cSmartTextbox value={v} onChange={setV} ghostStyle={{ color: '#0066cc', fontStyle: 'italic' }} /\u003e\n```\n\nFor richer markup (icons, badges) use the `renderGhost` render-prop. To style globally, target `[data-testid=\"smart-textbox-ghost\"]` / `[data-testid=\"smart-textarea-ghost\"]`.\n\n## Mobile\n\nTouch is a first-class target, not an afterthought. Things to know:\n\n- **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.\n- Wrappers use `touch-action: manipulation` to drop the 300ms tap delay.\n- `SmartSuggestion` selection runs on pointer events (mouse, touch, pen). Give items `min-height: 44px` to hit the WCAG touch target.\n- Inputs below `16px` font size make iOS Safari zoom on focus — keep `SmartTextbox` / `SmartTextarea` at 16px or larger.\n- Known limit: the `SmartSuggestion` dropdown can sit under the soft keyboard on short viewports. Portal the list or use a fullscreen picker there.\n\n## AI clients\n\n`SmartClient` is a capability-based interface. Use an adapter or implement your own:\n\n| Adapter | Import | When |\n| --- | --- | --- |\n| `createProxyClient` | `/adapters/proxy` | Production. POSTs to your backend; the key stays on the server. |\n| `createOpenAIClient` | `/adapters/openai` | Dev and demos only — never ship a key to the browser. |\n| `createAnthropicClient` | `/adapters/anthropic` | Anthropic Messages API (`complete` + `stream`). |\n| `createMockClient` | `/adapters/mock` | Tests and Storybook. |\n\nRolling your own is a few lines:\n\n```ts\nimport { SMART_CLIENT_PROTOCOL_VERSION, type SmartClient } from '@extedcoud/smart-components';\n\nconst myClient: SmartClient = {\n  protocolVersion: SMART_CLIENT_PROTOCOL_VERSION,\n  id: 'my-backend',\n  capabilities: new Set(['complete', 'stream']),\n  async complete(req) { /* ... */ return text; },\n  async *stream(req) { /* yield chunks */ },\n};\n```\n\n## Hooks\n\nThe components are thin wrappers over hooks. Reach for these to build your own on the same plumbing:\n\n```ts\nimport {\n  useGhostCompletion,\n  useSuggestionList,\n  useRewrite,\n  useSmartState,\n} from '@extedcoud/smart-components';\n```\n\n### `useSmartState` — useState that an LLM can fill\n\nA 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.\n\n```tsx\nconst [user, setUser, ai] = useSmartState(\n  { name: '', age: 0, bio: '' },\n  'a fictitious cyberpunk character',\n);\n\nai.generate();          // → { name: 'Kael-9', age: 28, bio: '…' }\nsetUser({ ... });       // still plain useState\n```\n\nWhen the seed is empty (`null`, `[]`), pass `options.shape` so it knows what to ask for:\n\n```tsx\nconst [tags, , ai] = useSmartState\u003cstring[]\u003e([], 'tags for a sci-fi blog post', {\n  shape: { type: 'array', item: 'string' },\n});\n```\n\nA few things worth knowing:\n\n- Calling `setValue` mid-generate cancels the in-flight request — the user's edit wins.\n- An empty inner array (`{ tags: [] }`) throws at mount; seed it or pass `shape`. Same for `Date`, `Map`, `Set`, functions — JSON-serializable values only.\n- Results are cached LRU(16) by `(shape, context)`. Opt out with `{ cache: false }`.\n- Generate is atomic (no streaming) and needs a client with the `complete` capability.\n\nFull walkthrough — form autofill, text extraction, palettes — is in the [docs](https://extedcoud.github.io/smart-components/).\n\n## Develop\n\nA pnpm workspace: the root is the library, `docs/` is the public site (Next.js + Fumadocs on GitHub Pages).\n\n```sh\npnpm install\npnpm storybook    # :6006 — internal dev surface\npnpm test         # vitest watch\npnpm lint\npnpm typecheck\npnpm build        # lib → dist/\n\n# Docs site (build the lib first — it consumes dist/)\npnpm --filter @extedcoud/smart-components build\npnpm --filter docs dev      # :3000\n```\n\nDocs deploy to GitHub Pages via `.github/workflows/deploy-docs.yml` on push to `main`.\n\n## Stack\n\n**Library:** Vite (lib mode), TypeScript, CSS Modules, ESLint 9, Prettier, Vitest + RTL, Storybook.\n**Docs:** Next.js 15 (static export), Fumadocs UI, Tailwind v4, MDX.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextedcoud%2Fsmart-components","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fextedcoud%2Fsmart-components","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextedcoud%2Fsmart-components/lists"}