import type { Meta, StoryObj } from 'storybook-solidjs-vite'; import { createSignal, For, Show } from 'solid-js'; import { PromptInput, PromptInputTextarea, PromptInputActions, PromptSuggestion, ModelSwitcher, Loader, Button, Attachments, Attachment, AttachmentPreview, AttachmentInfo, AttachmentRemove, } from '../index'; import type { ModelOption } from '../types'; import type { AttachmentData } from '../index'; import { ArrowUp, Paperclip, Globe, Mic, Square, Sparkles } from 'lucide-solid'; const meta: Meta = { title: 'Examples/Prompt Input Variants', parameters: { layout: 'padded' }, }; export default meta; type Story = StoryObj; export const BasicInput: Story = { name: 'Basic Input', render: () => { const [value, setValue] = createSignal(''); return (
setValue('')}>
); }, }; export const WithSuggestions: Story = { name: 'With Suggestion Chips', render: () => { const [value, setValue] = createSignal(''); const suggestionGroups = [ { label: 'Get started', items: ['Summarize this document', 'What are the key takeaways?', 'Create an outline'], }, { label: 'Go deeper', items: ['Compare with similar approaches', 'What are the tradeoffs?', 'Find contradictions'], }, ]; return (
{(group) => (
{group.label}
{(item) => ( setValue(item)}> {item} )}
)}
setValue('')}>
); }, }; export const WithActionButtons: Story = { name: 'With Action Buttons', render: () => { const [value, setValue] = createSignal(''); return (
setValue('')}>
); }, }; export const StreamingState: Story = { name: 'Streaming / Loading State', render: () => (

Disabled while streaming (with stop button)

Generating...

Disabled while waiting for first token

Thinking...
), }; export const WithModelSelector: Story = { name: 'With Model Selector', render: () => { const [value, setValue] = createSignal(''); const [modelId, setModelId] = createSignal('claude-4'); const models: ModelOption[] = [ { id: 'claude-4', name: 'Claude 4 Opus', provider: 'Anthropic' }, { id: 'claude-4-sonnet', name: 'Claude 4 Sonnet', provider: 'Anthropic' }, { id: 'gemini-2', name: 'Gemini 2.5 Pro', provider: 'Google' }, ]; return (
setValue('')}>
); }, }; export const WithFileAttachments: Story = { name: 'With File Attachments', render: () => { const [value, setValue] = createSignal(''); const [attachments, setAttachments] = createSignal([ { id: 'a1', type: 'file', filename: 'architecture.pdf', mediaType: 'application/pdf' }, { id: 'a2', type: 'file', filename: 'screenshot.png', mediaType: 'image/png', url: 'https://placehold.co/120x80/e2e8f0/94a3b8?text=PNG' }, ]); let fileInput: HTMLInputElement | undefined; const addFiles = (files: FileList | null) => { if (!files?.length) return; setAttachments((prev) => [ ...prev, ...Array.from(files).map((f) => ({ id: crypto.randomUUID(), type: 'file' as const, filename: f.name, mediaType: f.type || undefined, url: f.type.startsWith('image/') ? URL.createObjectURL(f) : undefined, })), ]); }; const removeAttachment = (id: string) => setAttachments((prev) => prev.filter((a) => a.id !== id)); const handleSubmit = () => { console.log('submit', { value: value(), attachments: attachments() }); setValue(''); setAttachments([]); }; return (
{ addFiles(e.currentTarget.files); e.currentTarget.value = ''; }} /> 0}>
{(att) => ( removeAttachment(att.id)}> )}

Note: the kc-prompt-input element handles the full attach UX automatically — this story shows how to wire the Solid primitives manually if you need full control.

); }, }; const SUGGESTION_GROUPS = [ { label: 'Get started', items: ['Summarize this document', 'What are the key takeaways?', 'Create an outline'] }, { label: 'Go deeper', items: ['Compare with similar approaches', 'What are the tradeoffs?', 'Find contradictions'] }, ]; const FULL_MODELS: ModelOption[] = [ { id: 'claude-4-opus', name: 'Claude 4 Opus', provider: 'Anthropic' }, { id: 'claude-4-sonnet', name: 'Claude 4 Sonnet', provider: 'Anthropic' }, { id: 'gemini-2', name: 'Gemini 2.5 Pro', provider: 'Google' }, ]; export const StoppableStreaming: Story = { name: 'Stoppable / Stop Button (kc-prompt-input)', render: () => { const [loading, setLoading] = createSignal(false); let timer: ReturnType | undefined; const handleSubmit = () => { setLoading(true); // Simulate a 3-second stream. In production: store the AbortController from // your fetch() call and call controller.abort() from onStop instead. timer = setTimeout(() => setLoading(false), 3000); }; const handleStop = () => { clearTimeout(timer); setLoading(false); }; return (

The Solid primitive equivalent of stoppable on kc-prompt-input: when loading is true, the send button is replaced by a Stop button (square icon). Clicking Stop fires kc-stop — your handler calls controller.abort() then clears the loading flag. Press Submit to start the simulated 3-second stream.

{}} > } >

Element usage: <kc-prompt-input stoppable loading> then listen for {' '}kc-stop to call controller.abort().

); }, }; export const FullExample: Story = { name: 'Full Example', render: () => { const [value, setValue] = createSignal(''); const [modelId, setModelId] = createSignal('claude-4-opus'); const [streaming, setStreaming] = createSignal(false); let abortRef: ReturnType | undefined; const handleSubmit = () => { if (!value().trim()) return; setStreaming(true); setValue(''); // Simulate a streaming reply that finishes after 3 s. // In production: store the AbortController from your fetch() call here, // then call controller.abort() from the Stop button handler instead. abortRef = setTimeout(() => setStreaming(false), 3000); }; const handleStop = () => { clearTimeout(abortRef); setStreaming(false); }; return (

Production-ready composer: model switcher, grouped suggestions, streaming state with Stop, and a send button that enables once you type. Submit → 3-second simulated stream → idle.

{(group) => (
{group.label}
{(item) => ( setValue(item)}> {item} )}
)}
} >
Generating…
} >
); }, };