@gram-ai/elements 1.12.0 → 1.13.1

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.
package/README.md CHANGED
@@ -110,6 +110,7 @@ The `ElementsConfig` object accepts the following configuration options:
110
110
  | `environment` | `Record<string, unknown>` | No | ✅ Implemented | `undefined` | Custom environment variable overrides for the MCP server. See [Gram documentation](https://www.speakeasy.com/docs/gram/host-mcp/public-private-servers#pass-through-authentication) for pass-through authentication |
111
111
  | `variant` | `'widget' \| 'sidecar' \| 'standalone'` | No | ✅ Implemented | `'widget'` | The layout variant for the chat interface. `widget` is a popup modal, `sidecar` is a side panel, `standalone` is a full-page experience |
112
112
  | `plugins` | [`Plugin[]`](#plugins) | No | ✅ Implemented | `[]` | Array of plugins to extend rendering capabilities. See [Plugins section](#plugins) for details |
113
+ | `components` | [`ComponentOverrides`](#component-overrides) | No | ✅ Implemented | `undefined` | Override default UI components with custom implementations. See [Component Overrides section](#component-overrides) for details |
113
114
  | `theme` | [`ThemeConfig`](#theme-configuration-themeconfig) | No | ✅ Implemented | `undefined` | Visual appearance configuration options |
114
115
  | `welcome` | [`WelcomeConfig`](#welcome-configuration-welcomeconfig) | Yes | ✅ Implemented | - | Configuration for the welcome message and initial suggestions |
115
116
  | `composer` | [`ComposerConfig`](#composer-configuration-composerconfig) | No | ✅ Implemented | `undefined` | Configuration for the composer input |
@@ -220,6 +221,160 @@ The following models are currently supported:
220
221
  | ------------ | ---------------------------------------------- | -------- | -------------- | ----------- | ------------------------------------------------------------------------------------------ |
221
222
  | `components` | `Record<string, ToolCallMessagePartComponent>` | No | ✅ Implemented | `undefined` | Override default React components for specific tool results. Tool names must match exactly |
222
223
 
224
+ ## Component Overrides
225
+
226
+ The `components` property in `ElementsConfig` allows you to override default UI components with your own custom implementations. This provides fine-grained control over the appearance and behavior of different parts of the chat interface.
227
+
228
+ ### Available Component Overrides
229
+
230
+ | Component | Type | Description |
231
+ | ------------------ | ------------------------------- | ------------------------------------------------------------ |
232
+ | `Composer` | `ComponentType` | The composer input area where users type messages |
233
+ | `UserMessage` | `ComponentType` | The entire user message bubble/container |
234
+ | `EditComposer` | `ComponentType` | The composer shown when editing a user message |
235
+ | `AssistantMessage` | `ComponentType` | The entire assistant message bubble/container |
236
+ | `ThreadWelcome` | `ComponentType` | The welcome screen shown when the thread is empty |
237
+ | `Text` | `TextMessagePartComponent` | Text content within messages (supports Markdown by default) |
238
+ | `Image` | `ImageMessagePartComponent` | Image attachments within messages |
239
+ | `ToolFallback` | `ToolCallMessagePartComponent` | Default component for tool results without custom components |
240
+ | `Reasoning` | `ReasoningMessagePartComponent` | Individual reasoning steps shown in assistant messages |
241
+ | `ReasoningGroup` | `ReasoningGroupComponent` | Container for grouped reasoning steps |
242
+ | `ToolGroup` | `ComponentType` | Container for grouped tool calls |
243
+
244
+ ### Component Override Examples
245
+
246
+ #### Basic Text Override
247
+
248
+ Override how text is rendered in assistant messages:
249
+
250
+ ```typescript
251
+ import { ElementsConfig } from '@gram-ai/elements'
252
+ import { useAssistantState } from '@assistant-ui/react'
253
+
254
+ const config: ElementsConfig = {
255
+ projectSlug: 'my-project',
256
+ mcp: 'https://app.getgram.ai/mcp/my-mcp-slug',
257
+ welcome: {
258
+ title: 'Hello!',
259
+ subtitle: 'How can I help you today?',
260
+ },
261
+ components: {
262
+ Text: () => {
263
+ const message = useAssistantState(({ message }) => message)
264
+ const text = message.parts
265
+ .map((part) => (part.type === 'text' ? part.text : ''))
266
+ .join('')
267
+
268
+ return (
269
+ <div className="custom-text">
270
+ {text}
271
+ </div>
272
+ )
273
+ },
274
+ },
275
+ }
276
+ ```
277
+
278
+ #### Custom Welcome Screen
279
+
280
+ Override the welcome screen with your own design:
281
+
282
+ ```typescript
283
+ import { ElementsConfig } from '@gram-ai/elements'
284
+
285
+ const CustomWelcome = () => {
286
+ return (
287
+ <div className="flex flex-col items-center justify-center h-full">
288
+ <h1 className="text-4xl font-bold mb-4">Welcome to My AI Assistant!</h1>
289
+ <p className="text-gray-600">Start chatting below</p>
290
+ </div>
291
+ )
292
+ }
293
+
294
+ const config: ElementsConfig = {
295
+ projectSlug: 'my-project',
296
+ mcp: 'https://app.getgram.ai/mcp/my-mcp-slug',
297
+ welcome: {
298
+ title: 'Hello!',
299
+ subtitle: 'How can I help you today?',
300
+ },
301
+ components: {
302
+ ThreadWelcome: CustomWelcome,
303
+ },
304
+ }
305
+ ```
306
+
307
+ #### Custom Message Containers
308
+
309
+ Override the entire message container for more control:
310
+
311
+ ```typescript
312
+ import { ElementsConfig } from '@gram-ai/elements'
313
+ import { MessagePrimitive } from '@assistant-ui/react'
314
+
315
+ const CustomUserMessage = () => {
316
+ return (
317
+ <MessagePrimitive.Root>
318
+ <div className="my-custom-user-message">
319
+ <div className="message-avatar">👤</div>
320
+ <div className="message-content">
321
+ <MessagePrimitive.Parts />
322
+ </div>
323
+ </div>
324
+ </MessagePrimitive.Root>
325
+ )
326
+ }
327
+
328
+ const config: ElementsConfig = {
329
+ projectSlug: 'my-project',
330
+ mcp: 'https://app.getgram.ai/mcp/my-mcp-slug',
331
+ welcome: {
332
+ title: 'Hello!',
333
+ subtitle: 'How can I help you today?',
334
+ },
335
+ components: {
336
+ UserMessage: CustomUserMessage,
337
+ },
338
+ }
339
+ ```
340
+
341
+ #### Combining Multiple Overrides
342
+
343
+ You can override multiple components at once:
344
+
345
+ ```typescript
346
+ import { ElementsConfig } from '@gram-ai/elements'
347
+ import {
348
+ CustomComposer,
349
+ CustomUserMessage,
350
+ CustomAssistantMessage,
351
+ CustomText,
352
+ } from './components'
353
+
354
+ const config: ElementsConfig = {
355
+ projectSlug: 'my-project',
356
+ mcp: 'https://app.getgram.ai/mcp/my-mcp-slug',
357
+ welcome: {
358
+ title: 'Hello!',
359
+ subtitle: 'How can I help you today?',
360
+ },
361
+ components: {
362
+ Composer: CustomComposer,
363
+ UserMessage: CustomUserMessage,
364
+ AssistantMessage: CustomAssistantMessage,
365
+ Text: CustomText,
366
+ },
367
+ }
368
+ ```
369
+
370
+ ### Notes on Component Overrides
371
+
372
+ - Component overrides give you full control over rendering, but you're responsible for maintaining the expected behavior
373
+ - When overriding message containers (`UserMessage`, `AssistantMessage`), make sure to include `MessagePrimitive.Root` and `MessagePrimitive.Parts` to maintain compatibility with the assistant-ui runtime
374
+ - For text content overrides, the default `Text` component supports Markdown rendering. If you override it, you'll need to implement your own Markdown support if needed
375
+ - Component overrides are applied globally across your chat interface
376
+ - For building custom components from scratch, refer to the [assistant-ui Context API documentation](https://www.assistant-ui.com/docs/guides/context-api) which provides detailed information about the state management system, available actions, and hooks like `useAssistantState`, `useAssistantApi`, and `useAssistantEvent`
377
+
223
378
  ### Configuration Examples
224
379
 
225
380
  #### Minimal Configuration
@@ -243,6 +398,7 @@ const config: ElementsConfig = {
243
398
  import { ElementsConfig } from '@gram-ai/elements'
244
399
  import { recommended } from '@gram-ai/elements/plugins'
245
400
  import { WeatherComponent } from './components/WeatherComponent'
401
+ import { CustomText } from './components/CustomText'
246
402
 
247
403
  const config: ElementsConfig = {
248
404
  systemPrompt: 'You are a helpful AI assistant.',
@@ -251,6 +407,9 @@ const config: ElementsConfig = {
251
407
  chatEndpoint: '/api/chat',
252
408
  variant: 'widget',
253
409
  plugins: recommended,
410
+ components: {
411
+ Text: CustomText,
412
+ },
254
413
  theme: {
255
414
  colorScheme: 'system',
256
415
  density: 'normal',
@@ -308,76 +467,6 @@ const config: ElementsConfig = {
308
467
  }
309
468
  ```
310
469
 
311
- #### Widget Variant with Custom Modal Icon
312
-
313
- ```typescript
314
- import { ElementsConfig } from '@gram-ai/elements'
315
- import { MessageSquare, X } from 'lucide-react'
316
-
317
- const config: ElementsConfig = {
318
- projectSlug: 'my-project',
319
- mcp: 'https://app.getgram.ai/mcp/my-mcp-slug',
320
- variant: 'widget',
321
- welcome: {
322
- title: 'Chat with us!',
323
- subtitle: 'Ask anything',
324
- },
325
- modal: {
326
- defaultOpen: true,
327
- icon: (state) => (state === 'open' ? <X /> : <MessageSquare />),
328
- },
329
- }
330
- ```
331
-
332
- #### Standalone Variant with Model Picker
333
-
334
- ```typescript
335
- import { ElementsConfig } from '@gram-ai/elements'
336
-
337
- const config: ElementsConfig = {
338
- projectSlug: 'my-project',
339
- mcp: 'https://app.getgram.ai/mcp/my-mcp-slug',
340
- variant: 'standalone',
341
- welcome: {
342
- title: 'AI Assistant',
343
- subtitle: 'Choose a model and start chatting',
344
- },
345
- composer: {
346
- placeholder: 'Ask me anything...',
347
- },
348
- model: {
349
- showModelPicker: true,
350
- defaultModel: 'openai/gpt-4o',
351
- },
352
- }
353
- ```
354
-
355
- #### Sidecar Variant with Custom Theme
356
-
357
- ```typescript
358
- import { ElementsConfig } from '@gram-ai/elements'
359
-
360
- const config: ElementsConfig = {
361
- projectSlug: 'my-project',
362
- mcp: 'https://app.getgram.ai/mcp/my-mcp-slug',
363
- variant: 'sidecar',
364
- theme: {
365
- colorScheme: 'dark',
366
- density: 'compact',
367
- radius: 'round',
368
- },
369
- welcome: {
370
- title: 'Support Chat',
371
- subtitle: "We're here to help",
372
- },
373
- sidecar: {
374
- title: 'Customer Support',
375
- width: '450px',
376
- expandedWidth: '900px',
377
- },
378
- }
379
- ```
380
-
381
470
  ## Plugins
382
471
 
383
472
  Plugins extend the Gram Elements library with custom rendering capabilities for specific content types. They allow you to transform markdown code blocks into rich, interactive visualizations and components.
@@ -2,7 +2,7 @@ import { VariantProps } from 'class-variance-authority';
2
2
  import * as React from 'react';
3
3
  declare const Button: React.ForwardRefExoticComponent<Omit<React.ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<(props?: ({
4
4
  variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
5
- size?: "icon" | "default" | "sm" | "lg" | "icon-sm" | "icon-lg" | null | undefined;
5
+ size?: "icon" | "sm" | "lg" | "default" | "icon-sm" | "icon-lg" | null | undefined;
6
6
  } & import('class-variance-authority/types').ClassProp) | undefined) => string> & {
7
7
  asChild?: boolean;
8
8
  }, "ref"> & React.RefAttributes<HTMLButtonElement>>;
@@ -1,4 +1,4 @@
1
1
  export declare const buttonVariants: (props?: ({
2
2
  variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
3
- size?: "icon" | "default" | "sm" | "lg" | "icon-sm" | "icon-lg" | null | undefined;
3
+ size?: "icon" | "sm" | "lg" | "default" | "icon-sm" | "icon-lg" | null | undefined;
4
4
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;