Skip to content

extedcouD/smart-components

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

16 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

@extedcoud/smart-components

npm bundle size 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.

Install

pnpm add @extedcoud/smart-components react react-dom

No runtime dependencies beyond React. Every adapter is built on plain fetch.

Quick start

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 (
    <SmartProvider client={client} model="gpt-4o-mini">
      <SmartTextbox value={v} onChange={setV} context="user is writing a support reply" />
      <SmartSuggestion
        value={v}
        onChange={setV}
        onSelect={(s) => console.log('picked', s)}
        context="naming a new project"
      />
    </SmartProvider>
  );
}

Components

Component What it does
<SmartTextbox> Single-line input with Copilot-style ghost completion. ArrowRight accepts (configurable), Esc dismisses.
<SmartTextarea> Multiline ghost completion via a mirror div, with multiline stops and optional auto-resize.
<SmartSuggestion> Combobox with an AI-generated dropdown. Arrow keys to move, Enter to pick.
<SmartRewrite> 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:

<SmartTextbox value={v} onChange={setV} ghostStyle={{ color: '#0066cc', fontStyle: 'italic' }} />

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:

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:

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.

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:

const [tags, , ai] = useSmartState<string[]>([], '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.

Develop

A pnpm workspace: the root is the library, docs/ is the public site (Next.js + Fumadocs on GitHub Pages).

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.

Releases

No releases published

Packages

 
 
 

Contributors