@gram-ai/elements 1.16.5 → 1.18.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.
package/README.md CHANGED
@@ -4,13 +4,6 @@ Elements is a library built for the agentic age. We provide customizable and ele
4
4
 
5
5
  ## Setup
6
6
 
7
- ### Package Exports
8
-
9
- This package provides two separate exports with different dependencies:
10
-
11
- - **`@gram-ai/elements`** - React UI components (requires React and related dependencies)
12
- - **`@gram-ai/elements/server`** - Server-side handlers (does NOT require React)
13
-
14
7
  ### Frontend Setup
15
8
 
16
9
  First ensure that you have installed the required peer dependencies:
@@ -48,7 +41,16 @@ const app = express()
48
41
 
49
42
  app.use(express.json())
50
43
 
51
- app.post('/chat/completions', handlers.chat)
44
+ app.post('/chat/session', (req, res) =>
45
+ handlers.session(req, res, {
46
+ // The origin from which the token will be used
47
+ embedOrigin: 'http://localhost:3000',
48
+ // A free-form user identifier
49
+ userIdentifier: 'user-123',
50
+ // Token expiration in seconds (max / default 3600)
51
+ expiresAfter: 3600,
52
+ })
53
+ )
52
54
  ```
53
55
 
54
56
  You will need to add an environment variable to your backend and make it available to the process:
@@ -71,8 +73,6 @@ import '@gram-ai/elements/elements.css'
71
73
  const config: ElementsConfig = {
72
74
  projectSlug: 'xxx',
73
75
  mcp: 'https://app.getgram.ai/mcp/{your_slug}',
74
- // Points to your backend endpoint
75
- chatEndpoint: '/chat/completions',
76
76
  variant: 'widget',
77
77
  welcome: {
78
78
  title: 'Hello!',
@@ -95,6 +95,37 @@ export const App = () => {
95
95
  }
96
96
  ```
97
97
 
98
+ ### Custom Session Endpoint
99
+
100
+ By default, Elements expects the session endpoint on your backend to be located at `/chat/session`. If you've mounted your session endpoint at a different path, you can provide a custom `getSession` function in the `ElementsProvider`:
101
+
102
+ ```jsx
103
+ import { GramElementsProvider, Chat, type ElementsConfig, type GetSessionFn } from '@gram-ai/elements'
104
+ import '@gram-ai/elements/elements.css'
105
+
106
+ const config: ElementsConfig = {
107
+ projectSlug: 'xxx',
108
+ mcp: 'https://app.getgram.ai/mcp/{your_slug}',
109
+ // ... other config
110
+ }
111
+
112
+ const customGetSession: GetSessionFn = async () => {
113
+ const response = await fetch('/api/custom-session-endpoint', {
114
+ method: 'POST',
115
+ })
116
+ const data = await response.json()
117
+ return data.client_token
118
+ }
119
+
120
+ export const App = () => {
121
+ return (
122
+ <GramElementsProvider config={config} getSession={customGetSession}>
123
+ <Chat />
124
+ </GramElementsProvider>
125
+ )
126
+ }
127
+ ```
128
+
98
129
  ## Configuration
99
130
 
100
131
  For complete configuration options and TypeScript type definitions, see the [API documentation](./docs/interfaces/ElementsConfig.md).
@@ -108,7 +139,6 @@ import '@gram-ai/elements/elements.css'
108
139
  const config: ElementsConfig = {
109
140
  projectSlug: 'your-project',
110
141
  mcp: 'https://app.getgram.ai/mcp/your-mcp-slug',
111
- chatEndpoint: '/chat/completions',
112
142
  variant: 'widget', // 'widget' | 'sidecar' | 'standalone'
113
143
  welcome: {
114
144
  title: 'Hello!',
@@ -0,0 +1,42 @@
1
+ import { Chat } from '.';
2
+ import { Meta, StoryFn } from '@storybook/react-vite';
3
+ import { ColorScheme, Density, Radius, Variant } from '../../types';
4
+ declare const meta: Meta<typeof Chat>;
5
+ export default meta;
6
+ type Story = StoryFn<typeof Chat>;
7
+ export declare const Default: Story;
8
+ interface PlaygroundArgs {
9
+ colorScheme: ColorScheme;
10
+ density: Density;
11
+ radius: Radius;
12
+ variant: Variant;
13
+ greeting: string;
14
+ subtitle: string;
15
+ starterPrompts: 'none' | 'some' | 'many';
16
+ placeholder: string;
17
+ attachments: boolean;
18
+ headerTitle: string;
19
+ }
20
+ export declare const ThemePlayground: StoryFn<PlaygroundArgs>;
21
+ export declare const VariantPlayground: Story;
22
+ export declare const Standalone: Story;
23
+ export declare const Sidecar: Story;
24
+ export declare const WithCustomModalIcon: Story;
25
+ export declare const WithCustomComposerPlaceholder: Story;
26
+ export declare const WithAttachmentsDisabled: Story;
27
+ export declare const WithCustomWelcomeMessage: Story;
28
+ export declare const WithCustomTools: Story;
29
+ export declare const WithModelPicker: Story;
30
+ export declare const WithExpandableModal: Story;
31
+ export declare const WithCustomModalTriggerPositionTopRight: Story;
32
+ export declare const WithCustomModalTriggerPositionBottomRight: Story;
33
+ export declare const WithCustomModalTriggerPositionBottomLeft: Story;
34
+ export declare const WithCustomModalTriggerPositionTopLeft: Story;
35
+ export declare const WithCustomSystemPrompt: Story;
36
+ export declare const WithChartPlugin: Story;
37
+ export declare const WithCustomComponentOverrides: Story;
38
+ export declare const WithFrontendTools: Story;
39
+ export declare const WithCustomLanguageModel: Story;
40
+ export declare const WithToolsRequiringApproval: Story;
41
+ export declare const WithMultipleGroupedToolsRequiringApproval: Story;
42
+ export declare const WithFrontendToolRequiringApproval: Story;
@@ -1,8 +1,9 @@
1
1
  import { ComponentPropsWithRef } from 'react';
2
2
  import { Button } from '../ui/button';
3
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
3
+ type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
4
4
  tooltip: string;
5
5
  side?: 'top' | 'bottom' | 'left' | 'right';
6
6
  align?: 'start' | 'center' | 'end';
7
7
  };
8
8
  export declare const TooltipIconButton: import('react').ForwardRefExoticComponent<Omit<TooltipIconButtonProps, "ref"> & import('react').RefAttributes<HTMLButtonElement>>;
9
+ export {};
@@ -1,5 +1,19 @@
1
+ import { BundledLanguage } from 'shiki';
1
2
  import * as React from 'react';
2
- type ToolStatus = 'pending' | 'running' | 'complete' | 'error';
3
+ type ToolStatus = 'pending' | 'running' | 'complete' | 'error' | 'approval';
4
+ type ContentItem = {
5
+ type: 'text';
6
+ text: string;
7
+ _meta?: {
8
+ 'getgram.ai/mime-type'?: string;
9
+ };
10
+ } | {
11
+ type: 'image';
12
+ data: string;
13
+ _meta?: {
14
+ 'getgram.ai/mime-type'?: string;
15
+ };
16
+ };
3
17
  interface ToolUIProps {
4
18
  /** Display name of the tool */
5
19
  name: string;
@@ -11,22 +25,60 @@ interface ToolUIProps {
11
25
  status?: ToolStatus;
12
26
  /** Request/input data - can be string or object */
13
27
  request?: string | Record<string, unknown>;
14
- /** Result/output data - can be string or object */
15
- result?: string | Record<string, unknown>;
28
+ /** Result/output data - can be string, object, or structured content array */
29
+ result?: string | Record<string, unknown> | {
30
+ content: ContentItem[];
31
+ };
16
32
  /** Whether the tool card starts expanded */
17
33
  defaultExpanded?: boolean;
18
34
  /** Additional class names */
19
35
  className?: string;
36
+ /** Approval callbacks */
37
+ onApproveOnce?: () => void;
38
+ onApproveForSession?: () => void;
39
+ onDeny?: () => void;
20
40
  }
21
41
  interface ToolUISectionProps {
22
42
  /** Section title */
23
43
  title: string;
24
44
  /** Content to display - string or object (will be JSON stringified) */
25
- content: string | Record<string, unknown>;
45
+ content: string | Record<string, unknown> | {
46
+ content: ContentItem[];
47
+ };
26
48
  /** Whether section starts expanded */
27
49
  defaultExpanded?: boolean;
50
+ /** Enable syntax highlighting */
51
+ highlightSyntax?: boolean;
52
+ /** Language hint for syntax highlighting */
53
+ language?: BundledLanguage;
28
54
  }
29
- declare function ToolUISection({ title, content, defaultExpanded, }: ToolUISectionProps): import("react/jsx-runtime").JSX.Element;
30
- declare function ToolUI({ name, icon, provider, status, request, result, defaultExpanded, className, }: ToolUIProps): import("react/jsx-runtime").JSX.Element;
31
- export { ToolUI, ToolUISection };
32
- export type { ToolUIProps, ToolUISectionProps, ToolStatus };
55
+ declare function StatusIndicator({ status }: {
56
+ status: ToolStatus;
57
+ }): import("react/jsx-runtime").JSX.Element;
58
+ declare function CopyButton({ content }: {
59
+ content: string;
60
+ }): import("react/jsx-runtime").JSX.Element;
61
+ declare function SyntaxHighlightedCode({ text, language, className, }: {
62
+ text: string;
63
+ language?: BundledLanguage;
64
+ className?: string;
65
+ }): import("react/jsx-runtime").JSX.Element;
66
+ declare function ToolUISection({ title, content, defaultExpanded, highlightSyntax, language, }: ToolUISectionProps): import("react/jsx-runtime").JSX.Element;
67
+ declare function ToolUI({ name, icon, provider, status, request, result, defaultExpanded, className, onApproveOnce, onApproveForSession, onDeny, }: ToolUIProps): import("react/jsx-runtime").JSX.Element;
68
+ interface ToolUIGroupProps {
69
+ /** Title for the group header */
70
+ title: string;
71
+ /** Optional icon */
72
+ icon?: React.ReactNode;
73
+ /** Overall status of the group */
74
+ status?: 'running' | 'complete';
75
+ /** Whether the group starts expanded */
76
+ defaultExpanded?: boolean;
77
+ /** Child tool UI components */
78
+ children: React.ReactNode;
79
+ /** Additional class names */
80
+ className?: string;
81
+ }
82
+ declare function ToolUIGroup({ title, icon, status, defaultExpanded, children, className, }: ToolUIGroupProps): import("react/jsx-runtime").JSX.Element;
83
+ export { ToolUI, ToolUISection, ToolUIGroup, SyntaxHighlightedCode, StatusIndicator, CopyButton, };
84
+ export type { ToolUIProps, ToolUISectionProps, ToolUIGroupProps, ToolStatus, ContentItem, };
@@ -0,0 +1,14 @@
1
+ import { Meta, StoryFn } from '@storybook/react-vite';
2
+ import { ToolUI } from './tool-ui';
3
+ declare const meta: Meta<typeof ToolUI>;
4
+ export default meta;
5
+ type Story = StoryFn<typeof ToolUI>;
6
+ export declare const Complete: Story;
7
+ export declare const Running: Story;
8
+ export declare const Pending: Story;
9
+ export declare const Error: Story;
10
+ export declare const WithoutProvider: Story;
11
+ export declare const WithCustomIcon: Story;
12
+ export declare const DefaultExpanded: Story;
13
+ export declare const LongContent: Story;
14
+ export declare const MultipleTools: Story;
@@ -0,0 +1,24 @@
1
+ import { ReactNode } from 'react';
2
+ interface PendingApproval {
3
+ toolCallId: string;
4
+ toolName: string;
5
+ args: unknown;
6
+ resolve: (approved: boolean) => void;
7
+ }
8
+ interface ToolApprovalContextType {
9
+ pendingApprovals: Map<string, PendingApproval>;
10
+ approvedTools: Set<string>;
11
+ requestApproval: (toolName: string, toolCallId: string, args: unknown) => Promise<boolean>;
12
+ /** Whitelist a tool name so all future calls are auto-approved */
13
+ whitelistTool: (toolName: string) => void;
14
+ /** Confirm a specific pending tool call approval */
15
+ confirmPendingApproval: (toolCallId: string) => void;
16
+ /** Reject a specific pending tool call approval */
17
+ rejectPendingApproval: (toolCallId: string) => void;
18
+ isToolApproved: (toolName: string) => boolean;
19
+ getPendingApproval: (toolCallId: string) => PendingApproval | undefined;
20
+ }
21
+ export declare function ToolApprovalProvider({ children }: {
22
+ children: ReactNode;
23
+ }): import("react/jsx-runtime").JSX.Element;
24
+ export type { ToolApprovalContextType };
@@ -0,0 +1,4 @@
1
+ import { ElementsContextType } from '../types';
2
+ import { ToolApprovalContextType } from './ToolApprovalContext';
3
+ export declare const ElementsContext: import('react').Context<ElementsContextType | undefined>;
4
+ export declare const ToolApprovalContext: import('react').Context<ToolApprovalContextType | null>;