@alquimia-ai/ui 2.2.2 → 2.2.4

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 (2) hide show
  1. package/README.md +330 -24
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,42 +1,348 @@
1
1
  # @alquimia-ai/ui
2
2
 
3
- UI components for Alquimia SDK.
3
+ The component half of the Alquimia frontend SDK. A React + Tailwind library built on shadcn/ui primitives: chat organisms, a themeable design system, and the renderer that turns an agent-authored component tree into real interactive UI.
4
4
 
5
- ## Table of Contents
5
+ It holds no runtime logic. State comes from [`@alquimia-ai/tools`](../tools) — typically `useAlquimia` — and these components render it.
6
6
 
7
- - [Installation](#installation)
8
- - [Usage](#usage)
9
- - [Available Components](#available-components)
7
+ - **Peer deps:** React ≥ 18, React DOM ≥ 18. Tailwind CSS for styling.
8
+ - **Not required:** `next`, `next-themes`. The package is framework-agnostic.
9
+
10
+ ---
11
+
12
+ ## Table of contents
13
+
14
+ - [Install and configure](#install-and-configure)
15
+ - [Theming](#theming)
16
+ - [Building a chat](#building-a-chat)
17
+ - [Chat organisms](#chat-organisms)
18
+ - [GenUI](#genui)
19
+ - [Speech](#speech)
20
+ - [Ratings](#ratings)
21
+ - [Documents and viewers](#documents-and-viewers)
22
+ - [Component inventory](#component-inventory)
23
+ - [Hooks and utilities](#hooks-and-utilities)
24
+ - [Export map](#export-map)
10
25
  - [Development](#development)
11
- - [Contributing](#contributing)
12
- - [License](#license)
13
26
 
14
- ## Installation
27
+ ---
15
28
 
16
- To install the `@alquimia-ai/ui` package, use the following command:
29
+ ## Install and configure
17
30
 
18
31
  ```sh
19
- yarn add @alquimia-ai/ui
32
+ npm install @alquimia-ai/tools @alquimia-ai/ui
33
+ npm install tailwindcss tailwindcss-animate
34
+ npm install framer-motion lucide-react # usually transitive
20
35
  ```
21
36
 
22
- Or with npm:
37
+ ### Tailwind
23
38
 
24
- ```sh
25
- npm install @alquimia-ai/ui
39
+ Use the package as a **preset**. One line replaces every colour, radius and animation token:
40
+
41
+ ```js
42
+ /** @type {import('tailwindcss').Config} */
43
+ module.exports = {
44
+ presets: [require('@alquimia-ai/ui/tailwind.config')],
45
+ content: [
46
+ './app/**/*.{ts,tsx}',
47
+ './components/**/*.{ts,tsx}',
48
+ './src/**/*.{ts,tsx}',
49
+ ],
50
+ };
51
+ ```
52
+
53
+ The preset already contributes the UI package's `dist` path to `content`, all token mappings, and the `tailwindcss-animate` plugin.
54
+
55
+ ### CSS
56
+
57
+ Import exactly one theme:
58
+
59
+ ```ts
60
+ import '@alquimia-ai/ui/styles/themes/base.css'; // neutral light + dark
61
+ import '@alquimia-ai/ui/styles/themes/base-alquimia.css'; // Alquimia brand
62
+ import '@alquimia-ai/ui/styles/themes/base-nordic.css';
63
+ import '@alquimia-ai/ui/styles/themes/base-primary.css';
64
+ ```
65
+
66
+ Optional stylesheets for specific surfaces: `styles/globals.css`, `prose.css`, `call-out.css`, `drawer.css`, `ratings.css`.
67
+
68
+ ---
69
+
70
+ ## Theming
71
+
72
+ Every component reads HSL CSS variables through Tailwind, so a theme is just a variable block. To use your own palette, skip the theme file and define them yourself:
73
+
74
+ ```css
75
+ @layer base {
76
+ :root {
77
+ --primary: 223 82% 60%;
78
+ --primary-foreground: 0 0% 98%;
79
+ --background: 0 0% 95%;
80
+ --foreground: 0 0% 3.9%;
81
+ --card: 0 0% 100%;
82
+ --muted: 0 0% 96.1%;
83
+ --muted-foreground: 0 0% 45.1%;
84
+ --border: 0 0% 85%;
85
+ --ring: 221 83% 53%;
86
+ --radius: 0.75rem;
87
+ }
88
+ }
89
+ ```
90
+
91
+ Dark mode is class-based. `AlquimiaUIProvider` toggles `.dark` on `<html>` and tracks the system preference — no `next-themes` dependency, and it coexists with one if you already have it:
92
+
93
+ ```tsx
94
+ import { AlquimiaUIProvider } from '@alquimia-ai/ui';
95
+
96
+ <AlquimiaUIProvider defaultMode="system"> {/* 'light' | 'dark' | 'system' */}
97
+ {children}
98
+ </AlquimiaUIProvider>
99
+ ```
100
+
101
+ `useAlquimiaTheme()` reads the resolved theme and lets you set it.
102
+
103
+ ---
104
+
105
+ ## Building a chat
106
+
107
+ The library favours composition over a monolith: an area that renders messages, a form that sends them, and slots for everything optional.
108
+
109
+ ```tsx
110
+ 'use client';
111
+ import { useRef } from 'react';
112
+ import { useAlquimia } from '@alquimia-ai/tools/hooks';
113
+ import { createNextJsAdapter } from '@alquimia-ai/tools/adapters/next';
114
+ import { AssistantMessageArea, AssistantInput } from '@alquimia-ai/ui/components/organisms';
115
+ import { ThinkIndicator } from '@alquimia-ai/ui/components/atoms';
116
+
117
+ const adapter = createNextJsAdapter();
118
+
119
+ export function Chat({ assistantId, conversationId }: Props) {
120
+ const messagesEndRef = useRef<HTMLDivElement>(null);
121
+ const alquimia = useAlquimia({ assistantId, adapter });
122
+
123
+ return (
124
+ <div className="flex h-full flex-col">
125
+ <AssistantMessageArea
126
+ messages={alquimia.messages.filter((m) => m.role !== 'system')}
127
+ messagesEndRef={messagesEndRef}
128
+ isLoading={alquimia.isMessageLoading}
129
+ isMessageStreaming={alquimia.isMessageStreaming}
130
+ streamingMessageId={alquimia.streamingMessageId}
131
+ thinkIndicator={<ThinkIndicator thoughts={['Thinking…']} />}
132
+ />
133
+
134
+ <AssistantInput
135
+ input={alquimia.input}
136
+ handleInputChange={alquimia.handleInputChange}
137
+ sendMessageFunc={(e) => alquimia.handleSubmit(e, undefined, conversationId)}
138
+ isMessageStreaming={alquimia.isMessageStreaming}
139
+ isButtonDisabled={!alquimia.input.trim()}
140
+ onFileDrop={alquimia.addAttachments}
141
+ />
142
+ </div>
143
+ );
144
+ }
26
145
  ```
27
146
 
28
- ## Available Components
147
+ `Assistant` wraps this in a shell with an optional title and description, and exports `AssistantHeader`, `AssistantTitle` and `AssistantDescription` for custom headers.
148
+
149
+ ---
150
+
151
+ ## Chat organisms
152
+
153
+ ### `AssistantMessageArea`
154
+
155
+ Renders the conversation: markdown, streaming text, timestamps, error states, tool cards and per-message actions.
156
+
157
+ | Prop | Type | Notes |
158
+ |---|---|---|
159
+ | `messages` | `AlquimiaMessage[]` | required |
160
+ | `messagesEndRef` | ref | scroll anchor |
161
+ | `isLoading` | `boolean` | required |
162
+ | `isMessageStreaming` | `boolean` | |
163
+ | `streamingMessageId` | `string \| null` | required — which bubble is live |
164
+ | `thinkIndicator` | `ReactNode` | replaces the default typing bubble |
165
+ | `actions` | `MessageAction[]` | per-message buttons (copy, rate, speak…) |
166
+ | `toolFactory` | `ToolFactory` | renders tool events inline |
167
+ | `showDetailedErrors` | `boolean` | surfaces `error_code` / `error_detail` |
168
+ | `handleIsTextStreaming` | `(streaming) => void` | typewriter lifecycle |
169
+ | `interleave` | `(afterIndex) => ReactNode` | inject nodes between messages — called with `0` before the first message and `i + 1` after message `i` |
170
+ | `awaitingUser` | `boolean` | suppresses the typing indicator while an interactive surface waits |
29
171
 
30
- - **Input**: A customizable input component.
31
- - **Select**: A customizable select component.
32
- - **Avatar**: A component for displaying user avatars.
33
- - **ScrollArea**: A component for creating scrollable areas.
34
- - **RichText**: A component for rendering rich text content.
35
- - **Skeleton**: A component for displaying skeleton loaders.
36
- - **Card**: A component for displaying card layouts.
37
- - **Drawer**: A component for creating drawers.
172
+ `interleave` and `awaitingUser` are the seams GenUI uses. The organism itself knows nothing about GenUI.
173
+
174
+ ### `AssistantInput`
175
+
176
+ A form with autosizing textarea, drag-and-drop and slots.
177
+
178
+ | Prop | Type | Notes |
179
+ |---|---|---|
180
+ | `input` | `string` | required |
181
+ | `handleInputChange` | `(e) => void` | required |
182
+ | `sendMessageFunc` | `(e) => Promise<void>` | required |
183
+ | `isMessageStreaming` | `boolean` | required |
184
+ | `isButtonDisabled` | `boolean` | required |
185
+ | `placeholders` | `[string, string]` | idle / streaming |
186
+ | `speechToTextComponent` | `ReactNode` | mic slot |
187
+ | `userToolboxComponent` | `ReactNode` | extra controls slot |
188
+ | `attachmentsSlot` | `ReactNode` | attachment chips |
189
+ | `onFileDrop` | `(files: File[]) => void` | enables drag-and-drop |
190
+
191
+ ---
192
+
193
+ ## GenUI
194
+
195
+ The agent answers with a **component tree**, not markup. `@alquimia-ai/tools` validates it against a catalog; this package renders it.
196
+
197
+ Batteries included:
198
+
199
+ ```tsx
200
+ import { AssistantChat } from '@alquimia-ai/ui/components/genui';
201
+
202
+ const alquimia = useAlquimia({ assistantId, adapter, genui: { allow: 'all' } });
203
+
204
+ <AssistantChat alquimia={alquimia} conversationId={conversationId} />
205
+ ```
38
206
 
207
+ `AssistantChat` is a thin composition of `AssistantMessageArea` + `AssistantInput` — it inherits markdown, streaming and error rendering rather than reimplementing them. Props: `alquimia` (required), `registry`, `conversationId`, `placeholder`, `suggestions`, `className`, `thinkIndicator`.
39
208
 
40
- ## License
209
+ Lower level, when you own the shell:
210
+
211
+ ```tsx
212
+ import { A2uiRenderer, coreUiRegistry } from '@alquimia-ai/ui/components/genui';
213
+
214
+ <A2uiRenderer
215
+ surface={surface}
216
+ registry={coreUiRegistry}
217
+ onAgentAction={alquimia.genui.onAgentAction}
218
+ onLocalAction={(action) => { /* client-only buttons */ }}
219
+ />
220
+ ```
221
+
222
+ The renderer owns the surface's data model, resolves `{ path }` bindings, and splits actions into agent-bound (posted back to the runtime, resuming the run) and local (handled in the browser).
223
+
224
+ `useAlquimia({ genui })` returns the controller that drives all of this: `surfaces` (what to render and after which message), `onAgentAction` (posts the result and resumes the run), `awaitingUser` (an interactive surface is waiting) and `dismiss`.
225
+
226
+ ### Your own components
227
+
228
+ `coreUiRegistry` maps catalog component names to React components — roughly 40 of them:
229
+
230
+ - **layout** — `Stack`, `Row`, `Column`, `Grid`, `Divider`, `Spacer`, `Card`, `Tabs`, `Accordion`
231
+ - **content** — `Text`, `Heading`, `Markdown`, `Image`, `Icon`, `Badge`, `Avatar`, `Alert`, `Stat`, `KeyValue`, `List`, `Quote`
232
+ - **inputs** — `TextField`, `TextArea`, `NumberField`, `Select`, `MultiSelect`, `Combobox`, `Checkbox`, `CheckboxGroup`, `Radio`, `Switch`, `Slider`, `DatePicker`, `DateRange`, `FileUpload`, `Rating`
233
+ - **data** — `Table`, `Chart`, `Progress`, `Timeline`
234
+ - **actions** — `Button`, `ButtonGroup`, `Link`
235
+
236
+ To render your design system instead, pass your catalog to the hook and your registry to the renderer. Each component receives `A2uiNodeProps`:
237
+
238
+ ```tsx
239
+ import type { A2uiNodeProps } from '@alquimia-ai/ui/components/genui';
240
+
241
+ function MyTextField({ node, value, onChange }: A2uiNodeProps) {
242
+ return <input value={String(value ?? '')} onChange={(e) => onChange?.(e.target.value)} />;
243
+ }
244
+
245
+ const registry = { ...coreUiRegistry, TextField: MyTextField };
246
+ ```
247
+
248
+ `{ node, value, onChange, onAction, children }` — the node's validated props are on `node`; binding and action plumbing is already resolved.
249
+
250
+ ---
251
+
252
+ ## Speech
253
+
254
+ Both components are presentational. The actual TTS/STT call is a function you pass in, usually from `useAlquimia`'s SDK, so the vendor stays your choice.
255
+
256
+ ```tsx
257
+ import { Whisper, SpeechToText } from '@alquimia-ai/ui/components/organisms';
258
+
259
+ // Read a message out loud
260
+ <Whisper
261
+ message={message}
262
+ isMessageStreaming={alquimia.isMessageStreaming}
263
+ textToSpeech={alquimia.sdk.textToSpeech}
264
+ />
265
+
266
+ // Dictate into the input — drop into AssistantInput's speechToTextComponent slot
267
+ <SpeechToText
268
+ RecordAudioIcon={<MicOff />}
269
+ IdleAudioIcon={<Mic />}
270
+ speechToText={alquimia.sdk.speechToText}
271
+ handleReplaceInput={alquimia.handleReplaceInput}
272
+ setIsAudioRecording={alquimia.setIsAudioRecording}
273
+ />
274
+ ```
275
+
276
+ ---
277
+
278
+ ## Ratings
279
+
280
+ `RatingDialog` (organism) plus `RatingStars`, `RatingThumbs` and `RatingComment` (molecules). Pair with `useRatings` from `@alquimia-ai/tools/hooks` and a `RatingsProvider` on the SDK.
281
+
282
+ ---
283
+
284
+ ## Documents and viewers
285
+
286
+ `DocumentSelector` and `DocumentViewer` under `/components/molecules/documents`; `PdfViewer` and `PlainTextViewer` under `/components/molecules/viewers`. Useful for rendering knowledge-base citations and uploaded attachments.
287
+
288
+ ---
289
+
290
+ ## Component inventory
291
+
292
+ **Atoms** — `Button`, `Input`, `Textarea`, `Label`, `Select`, `Checkbox`, `RadioGroup`, `Switch`, `Toggle`, `Slider`, `Calendar`, `Avatar`, `Badge`, `Alert`, `Card`, `Separator`, `Skeleton`, `Loader`, `Progress`, `Typography`, `RichText`, `ScrollArea`, `AspectRatio`, `Table`, `Tabs`, `Accordion`, `Breadcrumb`, `Popover`, `Dialog`, `Drawer`, `Command`, `Toast`, `Toaster`, `Chart`, `ThinkIndicator`.
293
+
294
+ **Molecules** — `PageContainer`, `AlertDialog`, `AssistantButton`, `Carousel`, `NavigationMenu`, `Sidebar`, `Sonner`, `CallOut`, rating and document components.
295
+
296
+ **Organisms** — `Assistant`, `AssistantMessageArea`, `AssistantInput`, `Whisper`, `SpeechToText`, `RatingDialog`.
297
+
298
+ **Templates** — `MessagesWindow`, `QueryBox`, `Hero`, `Cards`.
299
+
300
+ `Sidebar` and `Drawer` are what the reasoning/thinkings panel is built from, driven by `alquimia.hasThinkings` and the message `tooler` field.
301
+
302
+ ---
303
+
304
+ ## Hooks and utilities
305
+
306
+ | Export | Purpose |
307
+ |---|---|
308
+ | `useToast` | toast queue behind `Toaster` |
309
+ | `useDocument` | document viewer state |
310
+ | `useResizeObserver` | element size tracking |
311
+ | `useTextStreaming` | typewriter rendering for streamed text |
312
+ | `cn` | `clsx` + `tailwind-merge` class merge |
313
+ | `parseTextToSpeech` | strips characters that make TTS engines stumble |
314
+ | `blobToBase64` | file helper |
315
+
316
+ ---
317
+
318
+ ## Export map
319
+
320
+ | Entry | Contents |
321
+ |---|---|
322
+ | `.` | everything re-exported |
323
+ | `./components/atoms` | primitives |
324
+ | `./components/molecules` | composed components |
325
+ | `./components/molecules/viewers` | PDF / plain-text viewers |
326
+ | `./components/molecules/documents` | document selector + viewer |
327
+ | `./components/organisms` | chat, speech, ratings |
328
+ | `./components/templates` | page-level layouts |
329
+ | `./components/genui` | `A2uiRenderer`, `coreUiRegistry`, `AssistantChat` |
330
+ | `./components/hooks` | UI hooks |
331
+ | `./providers` | `AlquimiaUIProvider`, `useAlquimiaTheme` |
332
+ | `./lib/utils` | `cn` and helpers |
333
+ | `./types` | shared component types |
334
+ | `./styles/*`, `./styles/themes/*` | stylesheets |
335
+ | `./tailwind.config` | the Tailwind preset |
336
+ | `./styles.css` | bundled base styles |
337
+
338
+ ---
339
+
340
+ ## Development
341
+
342
+ ```sh
343
+ yarn test # vitest
344
+ yarn lint # eslint --max-warnings 0
345
+ yarn build # tsup
346
+ ```
41
347
 
42
- This project is licensed under the MIT License. See the LICENSE file for more details.
348
+ Storybook lives in [`apps/storybook`](../../apps/storybook). Changes to published behaviour need a changeset — see [`docs/how-to-use-changeset.md`](../../docs/how-to-use-changeset.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alquimia-ai/ui",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "author": "Alquimia AI",
5
5
  "description": "UI components for Alquimia SDK",
6
6
  "private": false,