@gram-ai/elements 1.18.4 → 1.18.6

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 (87) hide show
  1. package/dist/components/Chat/stories/Sidecar.stories.d.ts +6 -0
  2. package/dist/elements.cjs +23 -22
  3. package/dist/elements.cjs.map +1 -0
  4. package/dist/elements.js +609 -599
  5. package/dist/elements.js.map +1 -0
  6. package/dist/index-Bj7jPiuy.cjs +1 -0
  7. package/dist/index-Bj7jPiuy.cjs.map +1 -0
  8. package/dist/index-CJRypLIa.js +1 -0
  9. package/dist/index-CJRypLIa.js.map +1 -0
  10. package/dist/lib/api.d.ts +2 -0
  11. package/dist/lib/api.test.d.ts +1 -0
  12. package/dist/plugins.cjs +1 -0
  13. package/dist/plugins.cjs.map +1 -0
  14. package/dist/plugins.js +1 -0
  15. package/dist/plugins.js.map +1 -0
  16. package/dist/server.cjs +1 -0
  17. package/dist/server.cjs.map +1 -0
  18. package/dist/server.js +1 -0
  19. package/dist/server.js.map +1 -0
  20. package/package.json +6 -2
  21. package/src/components/Chat/index.tsx +21 -0
  22. package/src/components/Chat/stories/ColorScheme.stories.tsx +52 -0
  23. package/src/components/Chat/stories/Composer.stories.tsx +42 -0
  24. package/src/components/Chat/stories/Customization.stories.tsx +88 -0
  25. package/src/components/Chat/stories/Density.stories.tsx +52 -0
  26. package/src/components/Chat/stories/FrontendTools.stories.tsx +145 -0
  27. package/src/components/Chat/stories/Modal.stories.tsx +84 -0
  28. package/src/components/Chat/stories/Model.stories.tsx +32 -0
  29. package/src/components/Chat/stories/Plugins.stories.tsx +50 -0
  30. package/src/components/Chat/stories/Radius.stories.tsx +52 -0
  31. package/src/components/Chat/stories/Sidecar.stories.tsx +27 -0
  32. package/src/components/Chat/stories/ToolApproval.stories.tsx +110 -0
  33. package/src/components/Chat/stories/Tools.stories.tsx +175 -0
  34. package/src/components/Chat/stories/Variants.stories.tsx +46 -0
  35. package/src/components/Chat/stories/Welcome.stories.tsx +42 -0
  36. package/src/components/FrontendTools/index.tsx +9 -0
  37. package/src/components/assistant-ui/assistant-modal.tsx +255 -0
  38. package/src/components/assistant-ui/assistant-sidecar.tsx +88 -0
  39. package/src/components/assistant-ui/attachment.tsx +233 -0
  40. package/src/components/assistant-ui/markdown-text.tsx +240 -0
  41. package/src/components/assistant-ui/reasoning.tsx +261 -0
  42. package/src/components/assistant-ui/thread-list.tsx +97 -0
  43. package/src/components/assistant-ui/thread.tsx +632 -0
  44. package/src/components/assistant-ui/tool-fallback.tsx +111 -0
  45. package/src/components/assistant-ui/tool-group.tsx +59 -0
  46. package/src/components/assistant-ui/tooltip-icon-button.tsx +57 -0
  47. package/src/components/ui/avatar.tsx +51 -0
  48. package/src/components/ui/button.tsx +27 -0
  49. package/src/components/ui/buttonVariants.ts +33 -0
  50. package/src/components/ui/collapsible.tsx +31 -0
  51. package/src/components/ui/dialog.tsx +141 -0
  52. package/src/components/ui/popover.tsx +46 -0
  53. package/src/components/ui/skeleton.tsx +13 -0
  54. package/src/components/ui/tool-ui.stories.tsx +146 -0
  55. package/src/components/ui/tool-ui.tsx +676 -0
  56. package/src/components/ui/tooltip.tsx +61 -0
  57. package/src/contexts/ElementsProvider.tsx +256 -0
  58. package/src/contexts/ToolApprovalContext.tsx +120 -0
  59. package/src/contexts/contexts.ts +10 -0
  60. package/src/global.css +136 -0
  61. package/src/hooks/useAuth.ts +71 -0
  62. package/src/hooks/useDensity.ts +110 -0
  63. package/src/hooks/useElements.ts +14 -0
  64. package/src/hooks/useExpanded.ts +20 -0
  65. package/src/hooks/useMCPTools.ts +73 -0
  66. package/src/hooks/usePluginComponents.ts +34 -0
  67. package/src/hooks/useRadius.ts +42 -0
  68. package/src/hooks/useSession.ts +38 -0
  69. package/src/hooks/useThemeProps.ts +24 -0
  70. package/src/hooks/useToolApproval.ts +16 -0
  71. package/src/index.ts +45 -0
  72. package/src/lib/api.test.ts +90 -0
  73. package/src/lib/api.ts +8 -0
  74. package/src/lib/auth.ts +10 -0
  75. package/src/lib/easing.ts +1 -0
  76. package/src/lib/humanize.ts +14 -0
  77. package/src/lib/models.ts +22 -0
  78. package/src/lib/tools.ts +210 -0
  79. package/src/lib/utils.ts +16 -0
  80. package/src/plugins/README.md +49 -0
  81. package/src/plugins/chart/component.tsx +102 -0
  82. package/src/plugins/chart/index.ts +27 -0
  83. package/src/plugins/index.ts +7 -0
  84. package/src/server.ts +89 -0
  85. package/src/types/index.ts +726 -0
  86. package/src/types/plugins.ts +65 -0
  87. package/src/vite-env.d.ts +12 -0
@@ -0,0 +1,726 @@
1
+ import { MODELS } from '@/lib/models'
2
+ import {
3
+ AssistantTool,
4
+ ImageMessagePartComponent,
5
+ ReasoningGroupComponent,
6
+ ReasoningMessagePartComponent,
7
+ TextMessagePartComponent,
8
+ ToolCallMessagePartComponent,
9
+ } from '@assistant-ui/react'
10
+ import { LanguageModel } from 'ai'
11
+ import {
12
+ ComponentType,
13
+ Dispatch,
14
+ PropsWithChildren,
15
+ SetStateAction,
16
+ type ReactNode,
17
+ } from 'react'
18
+ import type { Plugin } from './plugins'
19
+
20
+ /**
21
+ * Function to retrieve the session token from the backend endpoint.
22
+ * Override this if you have mounted your session endpoint at a different path.
23
+ */
24
+ export type GetSessionFn = (init: { projectSlug: string }) => Promise<string>
25
+
26
+ type ServerUrl = string
27
+
28
+ export const VARIANTS = ['widget', 'sidecar', 'standalone'] as const
29
+ export type Variant = (typeof VARIANTS)[number]
30
+
31
+ /**
32
+ * The top level configuration object for the Elements library.
33
+ *
34
+ * @example
35
+ * const config: ElementsConfig = {
36
+ * mcp: 'https://app.getgram.ai/mcp/your-mcp-slug',
37
+ * projectSlug: 'my-project',
38
+ * systemPrompt: 'You are a helpful assistant.',
39
+ * }
40
+ */
41
+ export interface ElementsConfig {
42
+ /**
43
+ * The system prompt to use for the Elements library.
44
+ */
45
+ systemPrompt?: string
46
+
47
+ /**
48
+ * Any plugins to use for the Elements library.
49
+ *
50
+ * @default import { recommended } from '@gram-ai/elements/plugins'
51
+ */
52
+ plugins?: Plugin[]
53
+
54
+ /**
55
+ * Override the default components used by the Elements library.
56
+ *
57
+ * The available components are:
58
+ * - Composer
59
+ * - UserMessage
60
+ * - EditComposer
61
+ * - AssistantMessage
62
+ * - ThreadWelcome
63
+ * - Text
64
+ * - Image
65
+ * - ToolFallback
66
+ * - Reasoning
67
+ * - ReasoningGroup
68
+ * - ToolGroup
69
+ *
70
+ * To understand how to override these components, please consult the [assistant-ui documentation](https://www.assistant-ui.com/docs).
71
+ *
72
+ * @example
73
+ * const config: ElementsConfig = {
74
+ * components: {
75
+ * Composer: CustomComposerComponent,
76
+ * },
77
+ * }
78
+ */
79
+ components?: ComponentOverrides
80
+
81
+ /**
82
+ * The project slug to use for the Elements library.
83
+ *
84
+ * Your project slug can be found within the Gram dashboard.
85
+ *
86
+ * @example
87
+ * const config: ElementsConfig = {
88
+ * projectSlug: 'your-project-slug',
89
+ * }
90
+ */
91
+ projectSlug: string
92
+
93
+ /**
94
+ * The Gram Server URL to use for the Elements library.
95
+ * Can be retrieved from https://app.getgram.ai/{team}/{project}/mcp/{mcp_slug}
96
+ *
97
+ * Note: This config option will likely change in the future
98
+ *
99
+ * @example
100
+ * const config: ElementsConfig = {
101
+ * mcp: 'https://app.getgram.ai/mcp/your-mcp-slug',
102
+ * }
103
+ */
104
+ mcp?: ServerUrl
105
+
106
+ /**
107
+ * Custom environment variable overrides for the Elements library.
108
+ * Will be used to override the environment variables for the MCP server.
109
+ *
110
+ * For more documentation on passing through different kinds of environment variables, including bearer tokens, see the [Gram documentation](https://www.speakeasy.com/docs/gram/host-mcp/public-private-servers#pass-through-authentication).
111
+ */
112
+ environment?: Record<string, unknown>
113
+
114
+ /**
115
+ * The layout variant for the chat interface.
116
+ *
117
+ * - `widget`: A popup modal anchored to the bottom-right corner (default)
118
+ * - `sidecar`: A side panel that slides in from the right edge of the screen
119
+ * - `standalone`: A full-page chat experience
120
+ *
121
+ * @default 'widget'
122
+ */
123
+ variant?: Variant
124
+
125
+ /**
126
+ * LLM model configuration.
127
+ *
128
+ * @example
129
+ * const config: ElementsConfig = {
130
+ * model: {
131
+ * defaultModel: 'openai/gpt-4o',
132
+ * showModelPicker: true,
133
+ * },
134
+ * }
135
+ */
136
+ model?: ModelConfig
137
+
138
+ /**
139
+ * Visual appearance configuration options.
140
+ * Similar to OpenAI ChatKit's ThemeOption.\
141
+ *
142
+ * @example
143
+ * const config: ElementsConfig = {
144
+ * theme: {
145
+ * colorScheme: 'dark',
146
+ * density: 'compact',
147
+ * radius: 'round',
148
+ * },
149
+ * }
150
+ */
151
+ theme?: ThemeConfig
152
+
153
+ /**
154
+ * The configuration for the welcome message and initial suggestions.
155
+ *
156
+ * @example
157
+ * const config: ElementsConfig = {
158
+ * welcome: {
159
+ * title: 'Welcome to the chat',
160
+ * subtitle: 'This is a chat with a bot',
161
+ * suggestions: [
162
+ * { title: 'Suggestion 1', label: 'Suggestion 1', action: 'action1' },
163
+ * ],
164
+ * },
165
+ * }
166
+ */
167
+ welcome?: WelcomeConfig
168
+
169
+ /**
170
+ * The configuration for the composer.
171
+ *
172
+ * @example
173
+ * const config: ElementsConfig = {
174
+ * composer: {
175
+ * placeholder: 'Enter your message...',
176
+ * },
177
+ * }
178
+ */
179
+ composer?: ComposerConfig
180
+
181
+ /**
182
+ * Optional property to override the LLM provider. If you override the model,
183
+ * then logs & usage metrics will not be tracked directly via Gram.
184
+ *
185
+ * Please ensure that you are using an AI SDK v2 compatible model (e.g a
186
+ * Vercel AI sdk provider in the v2 semver range), as this is the only variant
187
+ * compatible with AI SDK V5
188
+ *
189
+ * Example with Google Gemini:
190
+ * ```ts
191
+ * import { google } from '@ai-sdk/google';
192
+ *
193
+ * const googleGemini = google('gemini-3-pro-preview');
194
+ *
195
+ * const config: ElementsConfig = {
196
+ * {other options}
197
+ * languageModel: googleGemini,
198
+ * }
199
+ * ```
200
+ */
201
+ languageModel?: LanguageModel
202
+
203
+ /**
204
+ * The configuration for the modal window.
205
+ * Only applicable if variant is 'widget'.
206
+ *
207
+ * @example
208
+ * const config: ElementsConfig = {
209
+ * modal: {
210
+ * title: 'Chat',
211
+ * position: 'bottom-right',
212
+ * expandable: true,
213
+ * defaultExpanded: false,
214
+ * dimensions: {
215
+ * default: {
216
+ * width: 400,
217
+ * height: 600,
218
+ * },
219
+ * },
220
+ * },
221
+ * }
222
+ */
223
+ modal?: ModalConfig
224
+
225
+ /**
226
+ * The configuration for the sidecar panel.
227
+ * Only applies if variant is 'sidecar'.
228
+ *
229
+ * @example
230
+ * const config: ElementsConfig = {
231
+ * sidecar: {
232
+ * title: 'Chat',
233
+ * expandable: true,
234
+ * defaultExpanded: false,
235
+ * dimensions: {
236
+ * default: {
237
+ * width: 400,
238
+ * height: 600,
239
+ * },
240
+ * },
241
+ * },
242
+ * }
243
+ */
244
+ sidecar?: SidecarConfig
245
+
246
+ /**
247
+ * The configuration for the tools.
248
+ *
249
+ * @example
250
+ * const config: ElementsConfig = {
251
+ * tools: {
252
+ * expandToolGroupsByDefault: true,
253
+ * frontendTools: {
254
+ * fetchUrl: FetchTool,
255
+ * },
256
+ * components: {
257
+ * fetchUrl: FetchToolComponent,
258
+ * },
259
+ * },
260
+ * }
261
+ */
262
+ tools?: ToolsConfig
263
+
264
+ api?: {
265
+ /**
266
+ * The Gram API URL to use for the Elements library.
267
+ *
268
+ * @example
269
+ * const config: ElementsConfig = {
270
+ * apiURL: 'https://api.getgram.ai',
271
+ * }
272
+ */
273
+ url?: string
274
+ } & AuthConfig
275
+ }
276
+
277
+ export type SessionAuthConfig = {
278
+ /**
279
+ * The function to use to retrieve the session token from the backend endpoint.
280
+ * By default, this will attempt to fetch the session token from `/chat/session`.
281
+ *
282
+ * @example
283
+ * const config: ElementsConfig = {
284
+ * api: {
285
+ * sessionFn: async () => {
286
+ * return fetch('/chat/session').then(res => res.json()).then(data => data.client_token)
287
+ * },
288
+ * },
289
+ * }
290
+ */
291
+ sessionFn: GetSessionFn
292
+ }
293
+
294
+ /**
295
+ * The API key auth config is used to authenticate the Elements library using an API key only.
296
+ *
297
+ * NOTE: This is not recommended for production use, and a warning
298
+ * will be displayed in the chat interface if you use this config.
299
+ * Define a session endpoint instead to avoid this warning.
300
+ *
301
+ * @example
302
+ * const config: ElementsConfig = {
303
+ * api: {
304
+ * UNSAFE_apiKey: 'your-api-key',
305
+ * },
306
+ * }
307
+ */
308
+ export type ApiKeyAuthConfig = {
309
+ /**
310
+ * The API key to use if you haven't yet configured a session endpoint.
311
+ * Do not use this in production.
312
+ *
313
+ * @example
314
+ * const config: ElementsConfig = {
315
+ * api: {
316
+ * UNSAFE_apiKey: 'your-api-key',
317
+ * },
318
+ * }
319
+ */
320
+ UNSAFE_apiKey: string
321
+ }
322
+
323
+ export type AuthConfig = SessionAuthConfig | ApiKeyAuthConfig | undefined
324
+
325
+ /**
326
+ * The LLM model to use for the Elements library.
327
+ *
328
+ * @example
329
+ * const config: ElementsConfig = {
330
+ * model: {
331
+ * defaultModel: 'openai/gpt-4o',
332
+ * },
333
+ * }
334
+ */
335
+ export type Model = (typeof MODELS)[number]
336
+
337
+ /**
338
+ * ModelConfig is used to configure model support in the Elements library.
339
+ *
340
+ */
341
+ export interface ModelConfig {
342
+ /**
343
+ * Whether to show the model picker in the composer.
344
+ */
345
+ showModelPicker?: boolean
346
+
347
+ /**
348
+ * The default model to use for the Elements library.
349
+ */
350
+ defaultModel?: Model
351
+ }
352
+
353
+ export const DENSITIES = ['compact', 'normal', 'spacious'] as const
354
+ export type Density = (typeof DENSITIES)[number]
355
+ export const COLOR_SCHEMES = ['light', 'dark', 'system'] as const
356
+ export type ColorScheme = (typeof COLOR_SCHEMES)[number]
357
+
358
+ export const RADII = ['round', 'soft', 'sharp'] as const
359
+ export type Radius = (typeof RADII)[number]
360
+
361
+ /**
362
+ * ThemeConfig provides visual appearance customization options.
363
+ * Inspired by OpenAI ChatKit's ThemeOption.
364
+ *
365
+ * @example
366
+ * const config: ElementsConfig = {
367
+ * theme: {
368
+ * colorScheme: 'dark',
369
+ * density: 'compact',
370
+ * radius: 'round',
371
+ * },
372
+ * }
373
+ */
374
+ export interface ThemeConfig {
375
+ /**
376
+ * The color scheme to use for the UI.
377
+ * @default 'light'
378
+ */
379
+ colorScheme?: ColorScheme
380
+
381
+ /**
382
+ * Determines the overall spacing of the UI.
383
+ * - `compact`: Reduced padding and margins for dense layouts
384
+ * - `normal`: Standard spacing (default)
385
+ * - `spacious`: Increased padding and margins for airy layouts
386
+ * @default 'normal'
387
+ */
388
+ density?: Density
389
+
390
+ /**
391
+ * Determines the overall roundness of the UI.
392
+ * - `round`: Large border radius
393
+ * - `soft`: Moderate border radius (default)
394
+ * - `sharp`: Minimal border radius
395
+ * @default 'soft'
396
+ */
397
+ radius?: Radius
398
+ }
399
+
400
+ export interface ComponentOverrides {
401
+ /**
402
+ * The component to use for the composer (the input area where users type messages)
403
+ */
404
+ Composer?: ComponentType
405
+ /**
406
+ * The component to use for the user message.
407
+ */
408
+ UserMessage?: ComponentType
409
+ /**
410
+ * The component to use for the edit composer (inline message editor)
411
+ */
412
+ EditComposer?: ComponentType
413
+ /**
414
+ * The component to use for the assistant message (messages generated by the LLM).
415
+ *
416
+ * Note: if you override this, the Text component will not be used.
417
+ */
418
+ AssistantMessage?: ComponentType
419
+ /**
420
+ * The component to use for the thread welcome.
421
+ */
422
+ ThreadWelcome?: ComponentType
423
+
424
+ // MessagePrimitive.Parts components
425
+ /**
426
+ * The component to use for the text message.
427
+ */
428
+ Text?: TextMessagePartComponent
429
+ /**
430
+ * The component to use for the image message.
431
+ */
432
+ Image?: ImageMessagePartComponent
433
+ /**
434
+ * The component to use for the tool fallback (default UI shown when a tool returns a result).
435
+ */
436
+ ToolFallback?: ToolCallMessagePartComponent
437
+ /**
438
+ * The component to use for the reasoning message.
439
+ */
440
+ Reasoning?: ReasoningMessagePartComponent
441
+ /**
442
+ * The component to use for the reasoning group.
443
+ */
444
+ ReasoningGroup?: ReasoningGroupComponent
445
+
446
+ /**
447
+ * The component to use for the tool group (a group of tool calls returned by the LLM in a single message).
448
+ */
449
+ ToolGroup?: ComponentType<
450
+ PropsWithChildren<{ startIndex: number; endIndex: number }>
451
+ >
452
+ }
453
+
454
+ /**
455
+ * ToolsConfig is used to configure tool support in the Elements library.
456
+ * At the moment, you can override the default React components used by
457
+ * individual tool results.
458
+ *
459
+ * @example
460
+ * const config: ElementsConfig = {
461
+ * tools: {
462
+ * components: {
463
+ * "get_current_weather": WeatherComponent,
464
+ * },
465
+ * },
466
+ * }
467
+ */
468
+
469
+ export interface ToolsConfig {
470
+ /**
471
+ * Whether individual tool calls within a group should be expanded by default.
472
+ * @default false
473
+ */
474
+ expandToolGroupsByDefault?: boolean
475
+
476
+ /**
477
+ * `components` can be used to override the default components used by the
478
+ * Elements library for a given tool result.
479
+ *
480
+ * Please ensure that the tool name directly matches the tool name in your Gram toolset.
481
+ *
482
+ * @example
483
+ * const config: ElementsConfig = {
484
+ * tools: {
485
+ * components: {
486
+ * "get_current_weather": WeatherComponent,
487
+ * },
488
+ * },
489
+ * }
490
+ */
491
+ components?:
492
+ | Record<string, ToolCallMessagePartComponent | undefined>
493
+ | undefined
494
+
495
+ /**
496
+ * The frontend tools to use for the Elements library.
497
+ *
498
+ * @example
499
+ * ```ts
500
+ * import { defineFrontendTool } from '@gram-ai/elements'
501
+ *
502
+ * const FetchTool = defineFrontendTool<{ url: string }, string>(
503
+ * {
504
+ * description: 'Fetch a URL (supports CORS-enabled URLs like httpbin.org)',
505
+ * parameters: z.object({
506
+ * url: z.string().describe('URL to fetch (must support CORS)'),
507
+ * }),
508
+ * execute: async ({ url }) => {
509
+ * const response = await fetch(url as string)
510
+ * const text = await response.text()
511
+ * return text
512
+ * },
513
+ * },
514
+ * 'fetchUrl'
515
+ * )
516
+ * const config: ElementsConfig = {
517
+ * tools: {
518
+ * frontendTools: {
519
+ * fetchUrl: FetchTool,
520
+ * },
521
+ * },
522
+ * }
523
+ * ```
524
+ *
525
+ * You can also override the default components used by the
526
+ * Elements library for a given tool result.
527
+ *
528
+ * @example
529
+ * ```ts
530
+ * import { FetchToolComponent } from './components/FetchToolComponent'
531
+ *
532
+ * const config: ElementsConfig = {
533
+ * tools: {
534
+ * frontendTools: {
535
+ * fetchUrl: FetchTool,
536
+ * },
537
+ * components: {
538
+ * 'fetchUrl': FetchToolComponent, // will override the default component used by the Elements library for the 'fetchUrl' tool
539
+ * },
540
+ * },
541
+ * }
542
+ * ```
543
+ */
544
+ frontendTools?: Record<string, AssistantTool>
545
+
546
+ /**
547
+ * List of tool names that require confirmation from the end user before
548
+ * being executed. The user can choose to approve once or approve for the
549
+ * entire session via the UI.
550
+ *
551
+ * @example
552
+ * ```ts
553
+ * tools: {
554
+ * toolsRequiringApproval: ['delete_file', 'send_email'],
555
+ * }
556
+ * ```
557
+ */
558
+ toolsRequiringApproval?: string[]
559
+ }
560
+
561
+ export interface WelcomeConfig {
562
+ /**
563
+ * The welcome message to display when the thread is empty.
564
+ */
565
+ title: string
566
+
567
+ /**
568
+ * The subtitle to display when the thread is empty.
569
+ */
570
+ subtitle: string
571
+
572
+ /**
573
+ * The suggestions to display when the thread is empty.
574
+ */
575
+ suggestions?: Suggestion[]
576
+ }
577
+
578
+ export interface Suggestion {
579
+ title: string
580
+ label: string
581
+ action: string
582
+ }
583
+
584
+ export interface Dimensions {
585
+ default: Dimension
586
+ expanded?: {
587
+ width: number | string
588
+ height: number | string
589
+ }
590
+ }
591
+
592
+ export interface Dimension {
593
+ width: number | string
594
+ height: number | string
595
+ maxHeight?: number | string
596
+ }
597
+
598
+ interface ExpandableConfig {
599
+ /**
600
+ * Whether the modal or sidecar can be expanded
601
+ */
602
+ expandable?: boolean
603
+
604
+ /**
605
+ * Whether the modal or sidecar should be expanded by default.
606
+ * @default false
607
+ */
608
+ defaultExpanded?: boolean
609
+
610
+ /**
611
+ * The dimensions for the modal or sidecar window.
612
+ */
613
+ dimensions?: Dimensions
614
+ }
615
+
616
+ export type ModalTriggerPosition =
617
+ | 'bottom-right'
618
+ | 'bottom-left'
619
+ | 'top-right'
620
+ | 'top-left'
621
+
622
+ export interface ModalConfig extends ExpandableConfig {
623
+ /**
624
+ * Whether to open the modal window by default.
625
+ */
626
+ defaultOpen?: boolean
627
+
628
+ /**
629
+ * The title displayed in the modal header.
630
+ * @default 'Chat'
631
+ */
632
+ title?: string
633
+
634
+ /**
635
+ * The position of the modal trigger
636
+ *
637
+ * @default 'bottom-right'
638
+ */
639
+ position?: ModalTriggerPosition
640
+
641
+ /**
642
+ * The icon to use for the modal window.
643
+ * Receives the current state of the modal window.
644
+ *
645
+ * @example
646
+ * ```ts
647
+ * import { MessageCircleIcon } from 'lucide-react'
648
+ * import { cn } from '@/lib/utils'
649
+ *
650
+ * const config: ElementsConfig = {
651
+ * modal: {
652
+ * icon: (state) => {
653
+ * return <div className={cn('size-6', state === 'open' ? 'rotate-90' : 'rotate-0')}>
654
+ * <MessageCircleIcon className="size-6" />
655
+ * </div>
656
+ * },
657
+ * },
658
+ * }
659
+ * ```
660
+ */
661
+ icon?: (state: 'open' | 'closed' | undefined) => ReactNode
662
+ }
663
+
664
+ export interface ComposerConfig {
665
+ /**
666
+ * The placeholder text for the composer input.
667
+ * @default 'Send a message...'
668
+ */
669
+ placeholder?: string
670
+
671
+ /**
672
+ * Configuration for file attachments in the composer.
673
+ * Set to `false` to disable attachments entirely.
674
+ * Set to `true` for default attachment behavior.
675
+ * Or provide an object for fine-grained control.
676
+ * @default true
677
+ */
678
+ attachments?: boolean | AttachmentsConfig
679
+ }
680
+
681
+ /**
682
+ * AttachmentsConfig provides fine-grained control over file attachments.
683
+ *
684
+ * Note: not yet implemented. Attachments are not supported yet.
685
+ */
686
+ export interface AttachmentsConfig {
687
+ /**
688
+ * Accepted file types. Can be MIME types or file extensions.
689
+ * @example ['image/*', '.pdf', '.docx']
690
+ */
691
+ accept?: string[]
692
+
693
+ /**
694
+ * Maximum number of files that can be attached at once.
695
+ * @default 10
696
+ */
697
+ maxCount?: number
698
+
699
+ /**
700
+ * Maximum file size in bytes.
701
+ * @default 104857600 (100MB)
702
+ */
703
+ maxSize?: number
704
+ }
705
+
706
+ export interface SidecarConfig extends ExpandableConfig {
707
+ /**
708
+ * The title displayed in the sidecar header.
709
+ * @default 'Chat'
710
+ */
711
+ title?: string
712
+ }
713
+
714
+ /**
715
+ * @internal
716
+ */
717
+ export type ElementsContextType = {
718
+ config: ElementsConfig
719
+ setModel: (model: Model) => void
720
+ model: Model
721
+ isExpanded: boolean
722
+ setIsExpanded: Dispatch<SetStateAction<boolean>>
723
+ isOpen: boolean
724
+ setIsOpen: (isOpen: boolean) => void
725
+ plugins: Plugin[]
726
+ }