@slipstream-ui/ai 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +25 -0
  2. package/dist/index.d.mts +579 -0
  3. package/dist/index.d.ts +579 -0
  4. package/dist/index.js +1778 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/index.mjs +1706 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/dist/manifests/attachment.manifest.json +33 -0
  9. package/dist/manifests/branch.manifest.json +65 -0
  10. package/dist/manifests/chain-thought.manifest.json +39 -0
  11. package/dist/manifests/code-block.manifest.json +50 -0
  12. package/dist/manifests/command-palette.manifest.json +80 -0
  13. package/dist/manifests/confirmation.manifest.json +143 -0
  14. package/dist/manifests/conversation.manifest.json +290 -0
  15. package/dist/manifests/inline-citation.manifest.json +46 -0
  16. package/dist/manifests/message-actions.manifest.json +35 -0
  17. package/dist/manifests/message.manifest.json +235 -0
  18. package/dist/manifests/prompt-input.manifest.json +334 -0
  19. package/dist/manifests/reasoning.manifest.json +177 -0
  20. package/dist/manifests/response.manifest.json +51 -0
  21. package/dist/manifests/source.manifest.json +78 -0
  22. package/dist/manifests/streaming-indicator.manifest.json +39 -0
  23. package/dist/manifests/suggestions.manifest.json +45 -0
  24. package/dist/manifests/tool-call.manifest.json +158 -0
  25. package/package.json +61 -3
  26. package/src/adapter/adapter.test.tsx +83 -0
  27. package/src/adapter/hooks.ts +82 -0
  28. package/src/adapter/index.ts +3 -0
  29. package/src/adapter/messages.ts +49 -0
  30. package/src/adapter/types.ts +24 -0
  31. package/src/components/attachment/Attachment.test.tsx +72 -0
  32. package/src/components/attachment/Attachment.tsx +84 -0
  33. package/src/components/attachment/AttachmentList.tsx +30 -0
  34. package/src/components/attachment/index.ts +5 -0
  35. package/src/components/branch/Branch.test.tsx +52 -0
  36. package/src/components/branch/Branch.tsx +77 -0
  37. package/src/components/branch/index.ts +2 -0
  38. package/src/components/chainthought/ChainThought.test.tsx +34 -0
  39. package/src/components/chainthought/ChainThought.tsx +45 -0
  40. package/src/components/chainthought/index.ts +2 -0
  41. package/src/components/code-block/CodeBlock.test.tsx +46 -0
  42. package/src/components/code-block/CodeBlock.tsx +95 -0
  43. package/src/components/code-block/index.ts +2 -0
  44. package/src/components/command-palette/CommandPalette.test.tsx +74 -0
  45. package/src/components/command-palette/CommandPalette.tsx +156 -0
  46. package/src/components/command-palette/index.ts +2 -0
  47. package/src/components/confirmation/Confirmation.test.tsx +97 -0
  48. package/src/components/confirmation/Confirmation.tsx +146 -0
  49. package/src/components/confirmation/index.ts +2 -0
  50. package/src/components/conversation/Conversation.test.tsx +49 -0
  51. package/src/components/conversation/Conversation.tsx +301 -0
  52. package/src/components/conversation/Conversation.welcome.test.tsx +73 -0
  53. package/src/components/conversation/index.ts +2 -0
  54. package/src/components/inline-citation/InlineCitation.test.tsx +42 -0
  55. package/src/components/inline-citation/InlineCitation.tsx +56 -0
  56. package/src/components/inline-citation/index.ts +2 -0
  57. package/src/components/message/Message.error-edit.test.tsx +60 -0
  58. package/src/components/message/Message.test.tsx +80 -0
  59. package/src/components/message/Message.tsx +205 -0
  60. package/src/components/message/index.ts +2 -0
  61. package/src/components/message-actions/MessageActions.test.tsx +77 -0
  62. package/src/components/message-actions/MessageActions.tsx +134 -0
  63. package/src/components/message-actions/index.ts +2 -0
  64. package/src/components/prompt-input/PromptInput.compound.test.tsx +150 -0
  65. package/src/components/prompt-input/PromptInput.test.tsx +74 -0
  66. package/src/components/prompt-input/PromptInput.tsx +584 -0
  67. package/src/components/prompt-input/index.ts +21 -0
  68. package/src/components/reasoning/Reasoning.duration.test.tsx +35 -0
  69. package/src/components/reasoning/Reasoning.test.tsx +58 -0
  70. package/src/components/reasoning/Reasoning.tsx +85 -0
  71. package/src/components/reasoning/index.ts +2 -0
  72. package/src/components/response/Response.test.tsx +69 -0
  73. package/src/components/response/Response.tsx +75 -0
  74. package/src/components/response/close-incomplete-markdown.ts +47 -0
  75. package/src/components/response/index.ts +3 -0
  76. package/src/components/source/Source.tsx +51 -0
  77. package/src/components/source/Sources.tsx +42 -0
  78. package/src/components/source/index.ts +5 -0
  79. package/src/components/source/source.test.tsx +72 -0
  80. package/src/components/streaming/StreamingIndicator.test.tsx +26 -0
  81. package/src/components/streaming/StreamingIndicator.tsx +46 -0
  82. package/src/components/streaming/index.ts +2 -0
  83. package/src/components/suggestion/Suggestion.test.tsx +43 -0
  84. package/src/components/suggestion/Suggestion.tsx +47 -0
  85. package/src/components/suggestion/index.ts +2 -0
  86. package/src/components/tool-call/ToolCall.test.tsx +45 -0
  87. package/src/components/tool-call/ToolCall.tsx +101 -0
  88. package/src/components/tool-call/index.ts +2 -0
  89. package/src/index.ts +29 -0
  90. package/src/manifests/attachment.manifest.json +33 -0
  91. package/src/manifests/branch.manifest.json +65 -0
  92. package/src/manifests/chain-thought.manifest.json +39 -0
  93. package/src/manifests/code-block.manifest.json +50 -0
  94. package/src/manifests/command-palette.manifest.json +80 -0
  95. package/src/manifests/confirmation.manifest.json +143 -0
  96. package/src/manifests/conversation.manifest.json +290 -0
  97. package/src/manifests/inline-citation.manifest.json +46 -0
  98. package/src/manifests/message-actions.manifest.json +35 -0
  99. package/src/manifests/message.manifest.json +235 -0
  100. package/src/manifests/prompt-input.manifest.json +334 -0
  101. package/src/manifests/reasoning.manifest.json +177 -0
  102. package/src/manifests/response.manifest.json +51 -0
  103. package/src/manifests/source.manifest.json +78 -0
  104. package/src/manifests/streaming-indicator.manifest.json +39 -0
  105. package/src/manifests/suggestions.manifest.json +45 -0
  106. package/src/manifests/tool-call.manifest.json +158 -0
  107. package/src/test/jest-axe.d.ts +16 -0
  108. package/src/test/setup.ts +18 -0
  109. package/src/test/vitest-matchers.d.ts +10 -0
  110. package/src/types/index.ts +40 -0
  111. package/README.md +0 -3
@@ -0,0 +1,334 @@
1
+ {
2
+ "id": "prompt-input",
3
+ "name": "PromptInput",
4
+ "tier": "molecule",
5
+ "description": "A textarea input for entering chat prompts with auto-resize, keyboard shortcuts, and send button",
6
+ "designIntent": "The PromptInput component provides a specialized input for chat interfaces. It handles auto-resizing as the user types, supports Enter to submit (Shift+Enter for new line), and integrates with the conversation flow. It's designed to be the primary way users interact with AI assistants.",
7
+ "props": [
8
+ {
9
+ "name": "onSubmit",
10
+ "type": "(value: string) => void",
11
+ "required": true,
12
+ "description": "Callback when the user submits the prompt",
13
+ "examples": [
14
+ "(value) => sendMessage(value)"
15
+ ]
16
+ },
17
+ {
18
+ "name": "placeholder",
19
+ "type": "string",
20
+ "defaultValue": "Type your message...",
21
+ "required": false,
22
+ "description": "Placeholder text to display when input is empty",
23
+ "examples": [
24
+ "Type your message...",
25
+ "Ask me anything...",
26
+ "Enter your prompt..."
27
+ ]
28
+ },
29
+ {
30
+ "name": "disabled",
31
+ "type": "boolean",
32
+ "defaultValue": "false",
33
+ "required": false,
34
+ "description": "Whether the input is disabled",
35
+ "examples": [
36
+ true,
37
+ false
38
+ ]
39
+ },
40
+ {
41
+ "name": "isLoading",
42
+ "type": "boolean",
43
+ "defaultValue": "false",
44
+ "required": false,
45
+ "description": "Whether a submission is currently in progress",
46
+ "examples": [
47
+ true,
48
+ false
49
+ ]
50
+ },
51
+ {
52
+ "name": "autoFocus",
53
+ "type": "boolean",
54
+ "defaultValue": "false",
55
+ "required": false,
56
+ "description": "Whether to auto-focus the input on mount",
57
+ "examples": [
58
+ true,
59
+ false
60
+ ]
61
+ },
62
+ {
63
+ "name": "maxLength",
64
+ "type": "number",
65
+ "required": false,
66
+ "description": "Maximum character count for the input",
67
+ "examples": [
68
+ 500,
69
+ 1000,
70
+ 2000
71
+ ]
72
+ },
73
+ {
74
+ "name": "showSendButton",
75
+ "type": "boolean",
76
+ "defaultValue": "true",
77
+ "required": false,
78
+ "description": "Whether to show the send button",
79
+ "examples": [
80
+ true,
81
+ false
82
+ ]
83
+ },
84
+ {
85
+ "name": "sendButtonText",
86
+ "type": "string",
87
+ "defaultValue": "Send",
88
+ "required": false,
89
+ "description": "Text or icon for the send button",
90
+ "examples": [
91
+ "Send",
92
+ "Submit",
93
+ "→"
94
+ ]
95
+ },
96
+ {
97
+ "name": "className",
98
+ "type": "string",
99
+ "required": false,
100
+ "description": "Additional CSS classes to apply to the container"
101
+ },
102
+ {
103
+ "name": "onStop",
104
+ "type": "() => void",
105
+ "required": false,
106
+ "description": "While loading, the send button becomes a stop button that calls this handler."
107
+ },
108
+ {
109
+ "name": "stopButtonText",
110
+ "type": "string",
111
+ "defaultValue": "Stop generating",
112
+ "required": false,
113
+ "description": "Accessible label for the stop button; enables localization."
114
+ },
115
+ {
116
+ "name": "attachments",
117
+ "type": "AttachmentItem[]",
118
+ "required": false,
119
+ "description": "Controlled attachment list shown as chips above the input"
120
+ },
121
+ {
122
+ "name": "onRemoveAttachment",
123
+ "type": "(id: string) => void",
124
+ "required": false,
125
+ "description": "Called when the user removes an attachment chip"
126
+ },
127
+ {
128
+ "name": "onAttachmentsChange",
129
+ "type": "(attachments: AttachmentItem[]) => void",
130
+ "required": false,
131
+ "description": "Called when files are added via drag-and-drop, paste, or picker"
132
+ },
133
+ {
134
+ "name": "hasError",
135
+ "type": "boolean",
136
+ "required": false,
137
+ "description": "Styles the input in an error state (e.g. after a rejected file)"
138
+ },
139
+ {
140
+ "name": "accept",
141
+ "type": "string",
142
+ "required": false,
143
+ "description": "MIME types accepted for attachments (e.g. \"image/*\")"
144
+ },
145
+ {
146
+ "name": "multiple",
147
+ "type": "boolean",
148
+ "required": false,
149
+ "description": "Allow selecting multiple files"
150
+ },
151
+ {
152
+ "name": "maxFiles",
153
+ "type": "number",
154
+ "required": false,
155
+ "description": "Maximum number of attachments allowed"
156
+ },
157
+ {
158
+ "name": "maxFileSize",
159
+ "type": "number",
160
+ "required": false,
161
+ "description": "Maximum file size in bytes; rejects larger files"
162
+ },
163
+ {
164
+ "name": "onAttachmentError",
165
+ "type": "(error: string) => void",
166
+ "required": false,
167
+ "description": "Called when an attachment is rejected (size, type, or count limit)"
168
+ },
169
+ {
170
+ "name": "globalDrop",
171
+ "type": "boolean",
172
+ "required": false,
173
+ "description": "Enable drop-anywhere-on-page file acceptance"
174
+ },
175
+ {
176
+ "name": "children",
177
+ "type": "ReactNode",
178
+ "required": false,
179
+ "description": "Custom content rendered inside the input row (e.g. tool buttons)"
180
+ },
181
+ {
182
+ "name": "tooltip",
183
+ "required": false,
184
+ "type": "string | { content: ReactNode; shortcut?: string }",
185
+ "description": "Tooltip content for a toolbar button; optionally includes a keyboard shortcut hint (PromptInput.Button sub-component)"
186
+ },
187
+ {
188
+ "name": "active",
189
+ "required": false,
190
+ "defaultValue": "false",
191
+ "type": "boolean",
192
+ "description": "Toggles the active state on a toolbar button (PromptInput.Button sub-component)"
193
+ }
194
+ ],
195
+ "examples": [
196
+ {
197
+ "title": "Basic prompt input",
198
+ "code": "<PromptInput onSubmit={(value) => console.log(value)} />",
199
+ "description": "Simple prompt input with default configuration"
200
+ },
201
+ {
202
+ "title": "With custom placeholder",
203
+ "code": "<PromptInput \n onSubmit={handleSubmit}\n placeholder=\"Ask me anything about coding...\"\n/>",
204
+ "description": "Prompt input with custom placeholder text"
205
+ },
206
+ {
207
+ "title": "With auto-focus",
208
+ "code": "<PromptInput \n onSubmit={handleSubmit}\n autoFocus={true}\n/>",
209
+ "description": "Auto-focuses the input when component mounts"
210
+ },
211
+ {
212
+ "title": "With character limit",
213
+ "code": "<PromptInput \n onSubmit={handleSubmit}\n maxLength={500}\n/>",
214
+ "description": "Limits input to 500 characters"
215
+ },
216
+ {
217
+ "title": "Without send button",
218
+ "code": "<PromptInput \n onSubmit={handleSubmit}\n showSendButton={false}\n/>",
219
+ "description": "Hides the send button (user must press Enter)"
220
+ },
221
+ {
222
+ "title": "With loading state",
223
+ "code": "<PromptInput \n onSubmit={handleSubmit}\n isLoading={isGenerating}\n disabled={isGenerating}\n/>",
224
+ "description": "Shows loading state and disables while AI is responding"
225
+ },
226
+ {
227
+ "title": "Cancellable generation",
228
+ "code": "<PromptInput onSubmit={send} isLoading={loading} onStop={() => abortRef.current.abort()} />"
229
+ }
230
+ ],
231
+ "antiPatterns": [
232
+ {
233
+ "title": "Don't use for non-chat input",
234
+ "explanation": "PromptInput is specifically for chat interactions, not general form input",
235
+ "example": "// Avoid: Using PromptInput for forms\n<PromptInput onSubmit={handleFormSubmit} />\n\n// Prefer: Use appropriate form input components\n<TextArea onChange={handleChange} />"
236
+ },
237
+ {
238
+ "title": "Don't ignore Enter key behavior",
239
+ "explanation": "Users expect Enter to submit. If you change this, communicate it clearly",
240
+ "example": "// Avoid: Confusing users by disabling Enter\n<PromptInput \n onSubmit={handleSubmit}\n // Users expect Enter to submit\n/>\n\n// If you need different behavior, document it clearly"
241
+ },
242
+ {
243
+ "title": "Don't validate after submission",
244
+ "explanation": "Disable the input when empty rather than validating in the handler",
245
+ "example": "// Avoid: Validating in onSubmit\n<PromptInput onSubmit={(value) => {\n if (!value) return; // Validate before\n sendMessage(value);\n}} />\n\n// Prefer: Disable button when empty\n<PromptInput \n onSubmit={sendMessage}\n disabled={!value}\n/>"
246
+ }
247
+ ],
248
+ "accessibility": [
249
+ {
250
+ "feature": "Keyboard navigation",
251
+ "implementation": "Full keyboard support with Enter to submit, Shift+Enter for new line",
252
+ "userRequirements": "Document keyboard shortcuts for users"
253
+ },
254
+ {
255
+ "feature": "Screen reader support",
256
+ "implementation": "Textarea has proper label and placeholder. Button state is announced",
257
+ "userRequirements": "Ensure placeholder text is descriptive and helpful"
258
+ },
259
+ {
260
+ "feature": "Form validation",
261
+ "implementation": "Button is disabled when input is empty, preventing invalid submissions",
262
+ "userRequirements": "Consider adding aria-invalid for custom validation if needed"
263
+ },
264
+ {
265
+ "feature": "Auto-resize",
266
+ "implementation": "Textarea automatically grows with content up to max-height",
267
+ "userRequirements": "Test with screen readers to ensure height changes are announced appropriately"
268
+ }
269
+ ],
270
+ "contentGuidelines": [
271
+ {
272
+ "contentType": "Placeholder text",
273
+ "guidelines": [
274
+ "Use action-oriented placeholder text",
275
+ "Be specific about what kind of input is expected",
276
+ "Keep placeholders concise (under 50 characters ideally)",
277
+ "Avoid generic placeholders like 'Enter text'",
278
+ "Consider the context of the conversation"
279
+ ],
280
+ "goodExamples": [
281
+ "Type your message...",
282
+ "Ask me anything about coding...",
283
+ "Describe what you'd like to build..."
284
+ ],
285
+ "badExamples": [
286
+ "Enter text here",
287
+ "Input",
288
+ "Type something",
289
+ "Lorem ipsum dolor sit amet"
290
+ ]
291
+ },
292
+ {
293
+ "contentType": "Send button",
294
+ "guidelines": [
295
+ "Use clear icons or text for the send button",
296
+ "Consider using an arrow icon (→) for a modern look",
297
+ "Show loading state when submitting",
298
+ "Disable button when input is empty"
299
+ ],
300
+ "goodExamples": [
301
+ "→",
302
+ "Send",
303
+ "Submit"
304
+ ],
305
+ "badExamples": [
306
+ "Click me",
307
+ "OK",
308
+ "Go"
309
+ ]
310
+ }
311
+ ],
312
+ "compositionRules": [
313
+ {
314
+ "component": "Conversation",
315
+ "relationship": "PromptInput is typically used below a Conversation component to add new messages",
316
+ "example": "Conversation displays messages, PromptInput submits new ones to the same messages array"
317
+ },
318
+ {
319
+ "component": "ChatLayout",
320
+ "relationship": "PromptInput is often part of a ChatLayout that combines Conversation and input",
321
+ "example": "ChatLayout wraps Conversation and PromptInput in a complete chat interface"
322
+ }
323
+ ],
324
+ "relatedComponents": [
325
+ "Conversation",
326
+ "ChatLayout",
327
+ "Button"
328
+ ],
329
+ "package": "@slipstream-ui/ai",
330
+ "importPath": "@slipstream-ui/ai",
331
+ "version": "0.0.1",
332
+ "sourceDir": "prompt-input",
333
+ "exportName": "PromptInput"
334
+ }
@@ -0,0 +1,177 @@
1
+ {
2
+ "id": "reasoning",
3
+ "name": "Reasoning",
4
+ "tier": "molecule",
5
+ "description": "A collapsible component for displaying AI reasoning steps and chain-of-thought processes",
6
+ "designIntent": "The Reasoning component displays the step-by-step thought process of an AI agent. It's collapsible to avoid cluttering the interface while still providing transparency into how the AI arrived at its conclusions.",
7
+ "props": [
8
+ {
9
+ "name": "steps",
10
+ "type": "ReasoningStep[]",
11
+ "required": true,
12
+ "description": "Array of reasoning steps to display",
13
+ "examples": [
14
+ "[{ id: '1', content: 'Analyzing the request...' }, { id: '2', content: 'Formulating response...' }]"
15
+ ]
16
+ },
17
+ {
18
+ "name": "collapsible",
19
+ "type": "boolean",
20
+ "defaultValue": "true",
21
+ "required": false,
22
+ "description": "Whether the reasoning can be collapsed/expanded",
23
+ "examples": [
24
+ true,
25
+ false
26
+ ]
27
+ },
28
+ {
29
+ "name": "defaultExpanded",
30
+ "type": "boolean",
31
+ "defaultValue": "false",
32
+ "required": false,
33
+ "description": "Whether the reasoning is expanded by default",
34
+ "examples": [
35
+ true,
36
+ false
37
+ ]
38
+ },
39
+ {
40
+ "name": "className",
41
+ "type": "string",
42
+ "required": false,
43
+ "description": "Additional CSS classes to apply"
44
+ },
45
+ {
46
+ "name": "duration",
47
+ "type": "number",
48
+ "required": false,
49
+ "description": "Elapsed reasoning time in seconds; renders a duration chip"
50
+ },
51
+ {
52
+ "name": "isStreaming",
53
+ "type": "boolean",
54
+ "required": false,
55
+ "description": "Shows a streaming indicator while reasoning is in progress"
56
+ }
57
+ ],
58
+ "examples": [
59
+ {
60
+ "title": "Basic reasoning",
61
+ "code": "<Reasoning \n steps={[\n { id: '1', content: 'I need to analyze the user\\'s request.' },\n { id: '2', content: 'The user wants to know about React components.' },\n { id: '3', content: 'I should provide a comprehensive answer.' }\n ]}\n/>",
62
+ "description": "Collapsible reasoning with multiple steps"
63
+ },
64
+ {
65
+ "title": "Expanded by default",
66
+ "code": "<Reasoning \n steps={steps}\n defaultExpanded={true}\n/>",
67
+ "description": "Reasoning that shows steps immediately"
68
+ },
69
+ {
70
+ "title": "Non-collapsible",
71
+ "code": "<Reasoning \n steps={steps}\n collapsible={false}\n/>",
72
+ "description": "Reasoning that cannot be collapsed"
73
+ },
74
+ {
75
+ "title": "With timestamps",
76
+ "code": "<Reasoning \n steps={[\n { id: '1', content: 'Step 1', timestamp: new Date() },\n { id: '2', content: 'Step 2', timestamp: new Date() }\n ]}\n/>",
77
+ "description": "Reasoning steps with timing information"
78
+ }
79
+ ],
80
+ "antiPatterns": [
81
+ {
82
+ "title": "Don't show reasoning for simple responses",
83
+ "explanation": "Reasoning is valuable for complex tasks but adds noise for simple responses.",
84
+ "example": "// Avoid: Showing reasoning for trivial responses\n<Reasoning steps={[\n { id: '1', content: 'I will say hello' }\n]} />\n<Message message={{ role: 'assistant', content: 'Hello!' }} />\n\n// Prefer: Only show reasoning for complex tasks\n<Reasoning steps={complexReasoning} />\n<Message message={{ role: 'assistant', content: complexResponse }} />"
85
+ },
86
+ {
87
+ "title": "Don't make steps too verbose",
88
+ "explanation": "Reasoning steps should be concise and focused on key decision points.",
89
+ "example": "// Avoid: Overly detailed steps\n{ id: '1', content: 'I am now processing the request by analyzing each word and considering multiple interpretations...' }\n\n// Prefer: Concise, clear steps\n{ id: '1', content: 'Analyzing the request for key terms and intent.' }"
90
+ },
91
+ {
92
+ "title": "Don't show empty reasoning",
93
+ "explanation": "The component returns null for empty arrays, but it's better to not render it at all.",
94
+ "example": "// Avoid: Rendering with no steps\n<Reasoning steps={[]} />\n\n// Prefer: Conditionally render\n{reasoningSteps.length > 0 && <Reasoning steps={reasoningSteps} />"
95
+ }
96
+ ],
97
+ "accessibility": [
98
+ {
99
+ "feature": "Collapsible state",
100
+ "implementation": "Uses aria-expanded to communicate collapsible state to screen readers",
101
+ "userRequirements": "Ensure the expanded/collapsed state is clear to all users"
102
+ },
103
+ {
104
+ "feature": "Keyboard navigation",
105
+ "implementation": "Collapsible button is keyboard accessible with Enter/Space to toggle",
106
+ "userRequirements": "Test keyboard navigation of the collapse/expand functionality"
107
+ },
108
+ {
109
+ "feature": "Visual hierarchy",
110
+ "implementation": "Steps are numbered and visually distinct with clear hierarchy",
111
+ "userRequirements": "Ensure step numbers and content have sufficient contrast"
112
+ }
113
+ ],
114
+ "contentGuidelines": [
115
+ {
116
+ "contentType": "Reasoning step content",
117
+ "guidelines": [
118
+ "Each step should represent a distinct decision or action",
119
+ "Use clear, concise language",
120
+ "Focus on the 'why' and 'how', not just the 'what'",
121
+ "Avoid overly technical jargon unless necessary",
122
+ "Structure steps logically (chronological or hierarchical)"
123
+ ],
124
+ "goodExamples": [
125
+ "Analyzing the user's request for key requirements",
126
+ "Considering multiple approaches to solve the problem",
127
+ "Selecting the most efficient solution based on constraints"
128
+ ],
129
+ "badExamples": [
130
+ "I am thinking about what to do",
131
+ "Processing...",
132
+ "Step 1",
133
+ "Lorem ipsum dolor sit amet"
134
+ ]
135
+ },
136
+ {
137
+ "contentType": "Step count",
138
+ "guidelines": [
139
+ "Show the total number of steps in the header",
140
+ "Number each step sequentially for easy reference",
141
+ "Keep step count reasonable (typically under 10 steps)",
142
+ "If there are many steps, consider grouping them"
143
+ ],
144
+ "goodExamples": [
145
+ "Reasoning (5 steps)",
146
+ "Step 1 of 5"
147
+ ],
148
+ "badExamples": [
149
+ "Reasoning",
150
+ "Step 1",
151
+ "Showing 47 steps"
152
+ ]
153
+ }
154
+ ],
155
+ "compositionRules": [
156
+ {
157
+ "component": "Message",
158
+ "relationship": "Reasoning is typically displayed before or within an assistant's message to show thought process",
159
+ "example": "Message component can contain Reasoning to show the AI's thinking before the final response"
160
+ },
161
+ {
162
+ "component": "ToolCall",
163
+ "relationship": "Reasoning often precedes tool calls to explain why a tool is being used",
164
+ "example": "Reasoning explains the decision to use a tool, ToolCall shows the actual tool invocation"
165
+ }
166
+ ],
167
+ "relatedComponents": [
168
+ "Message",
169
+ "ToolCall",
170
+ "Conversation"
171
+ ],
172
+ "package": "@slipstream-ui/ai",
173
+ "importPath": "@slipstream-ui/ai",
174
+ "version": "0.0.1",
175
+ "sourceDir": "reasoning",
176
+ "exportName": "Reasoning"
177
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "id": "response",
3
+ "name": "Response",
4
+ "tier": "molecule",
5
+ "description": "Streaming-optimized markdown renderer for AI responses with GFM support, streaming-safe repair of incomplete syntax, and highlighted code blocks with copy.",
6
+ "designIntent": "Render AI output as rich content without flicker or broken partial syntax while tokens arrive.",
7
+ "props": [
8
+ {
9
+ "name": "children",
10
+ "type": "string",
11
+ "required": true,
12
+ "description": "Markdown content to render."
13
+ },
14
+ {
15
+ "name": "parseIncompleteMarkdown",
16
+ "type": "boolean",
17
+ "defaultValue": true,
18
+ "required": false,
19
+ "description": "Close unclosed emphasis/inline-code/fences during streaming."
20
+ }
21
+ ],
22
+ "examples": [
23
+ {
24
+ "title": "Basic response",
25
+ "code": "<Response>{assistantMessage.content}</Response>"
26
+ },
27
+ {
28
+ "title": "Inside Message",
29
+ "code": "<Message message={m} renderContent={(c) => <Response>{c}</Response>} />"
30
+ }
31
+ ],
32
+ "antiPatterns": [
33
+ {
34
+ "title": "Do not use Response inside a code fence context",
35
+ "explanation": "Response is the top-level renderer; nested Response instances double-parse content."
36
+ }
37
+ ],
38
+ "accessibility": [
39
+ {
40
+ "feature": "Semantic HTML",
41
+ "implementation": "Headings, lists and tables are real elements announced correctly."
42
+ },
43
+ {
44
+ "feature": "Code blocks",
45
+ "implementation": "Routed through CodeBlock with accessible copy button."
46
+ }
47
+ ],
48
+ "package": "@slipstream-ui/ai",
49
+ "importPath": "@slipstream-ui/ai",
50
+ "version": "0.0.1"
51
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "id": "source",
3
+ "name": "Source / Sources",
4
+ "tier": "molecule",
5
+ "description": "Displays citations and references for an AI response. `Source` renders a single citation; `Sources` renders a numbered list with an overflow count.",
6
+ "designIntent": "Make generated answers transparent by showing where information came from.",
7
+ "props": [
8
+ {
9
+ "name": "index",
10
+ "type": "number",
11
+ "required": false,
12
+ "description": "1-based citation marker rendered as [n]; omit on Source to hide numbering."
13
+ },
14
+ {
15
+ "name": "source",
16
+ "type": "SourceItem",
17
+ "required": true,
18
+ "description": "The single source to render (Source component)"
19
+ },
20
+ {
21
+ "name": "onSelect",
22
+ "type": "(source: SourceItem) => void",
23
+ "required": false,
24
+ "description": "Called when the source chip is clicked"
25
+ },
26
+ {
27
+ "name": "sources",
28
+ "type": "SourceItem[]",
29
+ "required": true,
30
+ "description": "List of sources to render (Sources component)"
31
+ },
32
+ {
33
+ "name": "maxDisplay",
34
+ "type": "number",
35
+ "defaultValue": "3",
36
+ "required": false,
37
+ "description": "Maximum number of source chips to show before collapsing (Sources component)"
38
+ },
39
+ {
40
+ "name": "label",
41
+ "type": "string",
42
+ "defaultValue": "Sources",
43
+ "required": false,
44
+ "description": "Section label above the source list (Sources component)"
45
+ }
46
+ ],
47
+ "examples": [
48
+ {
49
+ "title": "Citations list",
50
+ "code": "<Sources sources={[{ id: '1', title: 'React docs', url: 'https://react.dev' }, { id: '2', title: 'MDN', url: 'https://developer.mozilla.org' }]} />"
51
+ },
52
+ {
53
+ "title": "Single inline citation",
54
+ "code": "<Source source={{ id: '1', title: 'React docs', url: 'https://react.dev' }} index={1} />"
55
+ }
56
+ ],
57
+ "antiPatterns": [
58
+ {
59
+ "title": "Do not nest Sources inside Sources",
60
+ "explanation": "Sources is a list container; compose individual Source items only when building a custom layout."
61
+ }
62
+ ],
63
+ "accessibility": [
64
+ {
65
+ "feature": "Link semantics",
66
+ "implementation": "References use native links with rel=noopener when URLs are provided."
67
+ },
68
+ {
69
+ "feature": "Decorative markers",
70
+ "implementation": "[n] citation markers are aria-hidden; titles are read as link text."
71
+ }
72
+ ],
73
+ "package": "@slipstream-ui/ai",
74
+ "importPath": "@slipstream-ui/ai",
75
+ "version": "0.0.1",
76
+ "sourceDir": "source",
77
+ "exportName": "Source"
78
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "id": "streaming-indicator",
3
+ "name": "StreamingIndicator",
4
+ "tier": "atom",
5
+ "description": "Shows that an AI response is currently streaming.",
6
+ "designIntent": "Give users immediate feedback while generated content is arriving.",
7
+ "props": [
8
+ {
9
+ "name": "isStreaming",
10
+ "type": "boolean",
11
+ "required": true,
12
+ "description": "Whether the indicator is visible."
13
+ },
14
+ {
15
+ "name": "text",
16
+ "type": "string",
17
+ "defaultValue": "Generating…",
18
+ "required": false,
19
+ "description": "Status text."
20
+ }
21
+ ],
22
+ "examples": [
23
+ {
24
+ "title": "Generating state",
25
+ "code": "<StreamingIndicator isStreaming={true} />"
26
+ }
27
+ ],
28
+ "accessibility": [
29
+ {
30
+ "feature": "Status text",
31
+ "implementation": "The state includes human-readable text in addition to animation."
32
+ }
33
+ ],
34
+ "package": "@slipstream-ui/ai",
35
+ "importPath": "@slipstream-ui/ai",
36
+ "version": "0.0.1",
37
+ "sourceDir": "streaming",
38
+ "exportName": "StreamingIndicator"
39
+ }