@gram-ai/elements 1.22.5 → 1.24.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 (90) hide show
  1. package/README.md +61 -0
  2. package/dist/components/Chat/stories/MessageFeedback.stories.d.ts +28 -0
  3. package/dist/components/Chat/stories/Plugins.stories.d.ts +6 -0
  4. package/dist/components/Chat/stories/Thread.stories.d.ts +15 -0
  5. package/dist/components/Chat/stories/Tools.stories.d.ts +15 -0
  6. package/dist/components/Replay.d.ts +11 -0
  7. package/dist/components/Replay.stories.d.ts +34 -0
  8. package/dist/components/assistant-ui/connection-status-indicator.d.ts +16 -0
  9. package/dist/components/assistant-ui/follow-on-suggestions.d.ts +6 -0
  10. package/dist/components/assistant-ui/message-feedback.d.ts +9 -0
  11. package/dist/components/ui/button.d.ts +1 -1
  12. package/dist/components/ui/buttonVariants.d.ts +1 -1
  13. package/dist/components/ui/generative-ui.d.ts +13 -0
  14. package/dist/contexts/ConnectionStatusContext.d.ts +27 -0
  15. package/dist/contexts/ReplayContext.d.ts +6 -0
  16. package/dist/contexts/ToolExecutionContext.d.ts +21 -0
  17. package/dist/elements.cjs +1 -1
  18. package/dist/elements.css +1 -1
  19. package/dist/elements.js +10 -8
  20. package/dist/hooks/useFollowOnSuggestions.d.ts +14 -0
  21. package/dist/hooks/useMCPTools.d.ts +4 -3
  22. package/dist/hooks/useRecordCassette.d.ts +32 -0
  23. package/dist/{index-D_ZJq5T1.js → index-B_7BIEfu.js} +14315 -12767
  24. package/dist/index-B_7BIEfu.js.map +1 -0
  25. package/dist/index-Bps9R2k6.cjs +178 -0
  26. package/dist/index-Bps9R2k6.cjs.map +1 -0
  27. package/dist/{index-BdXdd2ZM.js → index-CtyV0c-T.js} +8469 -7926
  28. package/dist/index-CtyV0c-T.js.map +1 -0
  29. package/dist/index-iUSSoKFz.cjs +251 -0
  30. package/dist/index-iUSSoKFz.cjs.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/lib/cassette.d.ts +46 -0
  33. package/dist/lib/generative-ui.d.ts +9 -0
  34. package/dist/plugins/components/PluginLoadingState.d.ts +11 -0
  35. package/dist/plugins/components/index.d.ts +1 -0
  36. package/dist/plugins/generative-ui/component.d.ts +3 -0
  37. package/dist/plugins/generative-ui/index.d.ts +6 -0
  38. package/dist/plugins/index.d.ts +1 -0
  39. package/dist/plugins.cjs +1 -1
  40. package/dist/plugins.js +3 -2
  41. package/dist/{profiler-Cucmmy3i.cjs → profiler-B6MySCOn.cjs} +2 -2
  42. package/dist/{profiler-Cucmmy3i.cjs.map → profiler-B6MySCOn.cjs.map} +1 -1
  43. package/dist/{profiler-CTZ-4zgJ.js → profiler-BWBC_MF5.js} +2 -2
  44. package/dist/{profiler-CTZ-4zgJ.js.map → profiler-BWBC_MF5.js.map} +1 -1
  45. package/dist/{startRecording-DnSD-PJG.js → startRecording-52IGlVhJ.js} +2 -2
  46. package/dist/{startRecording-DnSD-PJG.js.map → startRecording-52IGlVhJ.js.map} +1 -1
  47. package/dist/{startRecording-4CwQzWU_.cjs → startRecording-9LKBFNDH.cjs} +2 -2
  48. package/dist/{startRecording-4CwQzWU_.cjs.map → startRecording-9LKBFNDH.cjs.map} +1 -1
  49. package/dist/types/index.d.ts +37 -0
  50. package/package.json +10 -4
  51. package/src/components/Chat/stories/MessageFeedback.stories.tsx +169 -0
  52. package/src/components/Chat/stories/Plugins.stories.tsx +116 -0
  53. package/src/components/Chat/stories/Thread.stories.tsx +78 -0
  54. package/src/components/Chat/stories/Tools.stories.tsx +122 -0
  55. package/src/components/Replay.stories.tsx +326 -0
  56. package/src/components/Replay.tsx +241 -0
  57. package/src/components/assistant-ui/connection-status-indicator.tsx +134 -0
  58. package/src/components/assistant-ui/follow-on-suggestions.tsx +122 -0
  59. package/src/components/assistant-ui/markdown-text.tsx +6 -7
  60. package/src/components/assistant-ui/message-feedback.tsx +177 -0
  61. package/src/components/assistant-ui/thread.tsx +320 -79
  62. package/src/components/assistant-ui/tool-group.tsx +27 -16
  63. package/src/components/ui/generative-ui.tsx +437 -0
  64. package/src/components/ui/tool-ui.tsx +59 -17
  65. package/src/contexts/ConnectionStatusContext.tsx +158 -0
  66. package/src/contexts/ElementsProvider.tsx +140 -27
  67. package/src/contexts/ReplayContext.ts +7 -0
  68. package/src/contexts/ToolExecutionContext.tsx +101 -0
  69. package/src/global.css +14 -8
  70. package/src/hooks/useAuth.ts +3 -0
  71. package/src/hooks/useFollowOnSuggestions.ts +237 -0
  72. package/src/hooks/useMCPTools.ts +32 -12
  73. package/src/hooks/useRecordCassette.ts +79 -0
  74. package/src/index.ts +10 -0
  75. package/src/lib/cassette.ts +260 -0
  76. package/src/lib/generative-ui.ts +18 -0
  77. package/src/plugins/chart/component.tsx +46 -9
  78. package/src/plugins/components/PluginLoadingState.tsx +35 -0
  79. package/src/plugins/components/index.ts +1 -0
  80. package/src/plugins/generative-ui/component.tsx +56 -0
  81. package/src/plugins/generative-ui/index.ts +153 -0
  82. package/src/plugins/index.ts +3 -1
  83. package/src/types/index.ts +40 -0
  84. package/src/vite-env.d.ts +1 -0
  85. package/dist/index-BdXdd2ZM.js.map +0 -1
  86. package/dist/index-CNVoovK7.cjs +0 -111
  87. package/dist/index-CNVoovK7.cjs.map +0 -1
  88. package/dist/index-D_ZJq5T1.js.map +0 -1
  89. package/dist/index-Dip7A_UI.cjs +0 -147
  90. package/dist/index-Dip7A_UI.cjs.map +0 -1
package/README.md CHANGED
@@ -263,6 +263,67 @@ To create your own plugins, see the comprehensive [Plugin Development Guide](./s
263
263
  - Real-world examples
264
264
  - Troubleshooting tips
265
265
 
266
+ ## Replay
267
+
268
+ Replay lets you play back a pre-recorded conversation with streaming animations — no auth, MCP, or network calls required. This is useful for marketing demos, documentation, and testing.
269
+
270
+ ### Recording a cassette
271
+
272
+ A cassette is a JSON file that captures a conversation. To record one from a live chat session, enable the built-in recorder by setting this environment variable:
273
+
274
+ ```
275
+ VITE_ELEMENTS_ENABLE_CASSETTE_RECORDING=true
276
+ ```
277
+
278
+ This adds a record button to the composer. Click it to open the recorder popover, then start recording. Chat normally, and when you're done, click **Stop & Download** to save the cassette as a `.cassette.json` file.
279
+
280
+ You can also record programmatically using the `useRecordCassette` hook:
281
+
282
+ ```tsx
283
+ import {
284
+ GramElementsProvider,
285
+ Chat,
286
+ useRecordCassette,
287
+ } from '@gram-ai/elements'
288
+
289
+ function RecordableChat() {
290
+ const { isRecording, startRecording, download } = useRecordCassette()
291
+ return (
292
+ <>
293
+ <Chat />
294
+ <button onClick={startRecording}>Record</button>
295
+ <button onClick={() => download('demo')}>Save</button>
296
+ </>
297
+ )
298
+ }
299
+ ```
300
+
301
+ ### Playing back a cassette
302
+
303
+ Use the `<Replay>` component, which replaces `GramElementsProvider` entirely:
304
+
305
+ ```tsx
306
+ import { Replay, Chat } from '@gram-ai/elements'
307
+ import cassette from './demo.cassette.json'
308
+
309
+ function MarketingDemo() {
310
+ return (
311
+ <Replay cassette={cassette} config={{ variant: 'standalone' }}>
312
+ <Chat />
313
+ </Replay>
314
+ )
315
+ }
316
+ ```
317
+
318
+ `<Replay>` accepts optional timing props to control the playback speed:
319
+
320
+ | Prop | Default | Description |
321
+ | --------------------- | ------- | ----------------------------------------------- |
322
+ | `typingSpeed` | `15` | Milliseconds per character for streaming |
323
+ | `userMessageDelay` | `800` | Milliseconds before each user message appears |
324
+ | `assistantStartDelay` | `400` | Milliseconds before the assistant starts typing |
325
+ | `onComplete` | — | Callback when replay finishes |
326
+
266
327
  ## Contributing
267
328
 
268
329
  We welcome pull requests to Elements. Please read the contributing guide.
@@ -0,0 +1,28 @@
1
+ import { Chat } from '..';
2
+ import { Meta, StoryFn } from '@storybook/react-vite';
3
+ declare const meta: Meta<typeof Chat>;
4
+ export default meta;
5
+ type Story = StoryFn<typeof Chat>;
6
+ /**
7
+ * The feedback buttons appear on the last assistant message after it finishes streaming.
8
+ * Send a message to see the feedback UI animate in.
9
+ */
10
+ export declare const Default: Story;
11
+ /**
12
+ * Widget variant with feedback buttons.
13
+ */
14
+ export declare const Widget: Story;
15
+ /**
16
+ * Sidecar variant with feedback buttons.
17
+ */
18
+ export declare const Sidecar: Story;
19
+ /**
20
+ * Demonstrates feedback UI combined with follow-up suggestions.
21
+ * After the assistant responds, you'll see both AI-generated follow-up questions
22
+ * and feedback buttons (like/dislike) to mark the conversation as resolved.
23
+ */
24
+ export declare const WithFollowUpSuggestions: Story;
25
+ /**
26
+ * Standalone component demo showing the feedback buttons in isolation.
27
+ */
28
+ export declare const ComponentOnly: StoryFn;
@@ -4,3 +4,9 @@ declare const meta: Meta<typeof Chat>;
4
4
  export default meta;
5
5
  type Story = StoryFn<typeof Chat>;
6
6
  export declare const ChartPlugin: Story;
7
+ export declare const GenerativeUI: Story;
8
+ /**
9
+ * Demonstrates ActionButton in generative UI that triggers frontend tool calls.
10
+ * When a button is clicked, it directly executes the tool without an LLM roundtrip.
11
+ */
12
+ export declare const GenerativeUIWithActions: Story;
@@ -0,0 +1,15 @@
1
+ import { Meta, StoryFn } from '@storybook/react-vite';
2
+ import { Chat } from '..';
3
+ declare const meta: Meta<typeof Chat>;
4
+ export default meta;
5
+ type Story = StoryFn<typeof Chat>;
6
+ /**
7
+ * Demonstrates follow-up suggestions that appear after the assistant responds.
8
+ * Send a message and watch as AI-generated follow-up questions appear below the response.
9
+ */
10
+ export declare const FollowUpSuggestions: Story;
11
+ /**
12
+ * Shows the thread with follow-up suggestions disabled.
13
+ * No suggestions will appear after the assistant responds.
14
+ */
15
+ export declare const FollowUpSuggestionsDisabled: Story;
@@ -4,3 +4,18 @@ declare const meta: Meta<typeof Chat>;
4
4
  export default meta;
5
5
  type Story = StoryFn<typeof Chat>;
6
6
  export declare const CustomToolComponent: Story;
7
+ /**
8
+ * Demonstrates the generativeUI plugin which renders `ui` code blocks
9
+ * as dynamic UI widgets.
10
+ *
11
+ * The LLM outputs JSON in a ```ui code fence, and the plugin renders it
12
+ * using the built-in component catalog (Card, Grid, Metric, Table, etc.)
13
+ */
14
+ export declare const GenerativeUI: Story;
15
+ /**
16
+ * Demonstrates ActionButton in generative UI that triggers tool calls.
17
+ *
18
+ * The LLM generates UI with ActionButton components that, when clicked,
19
+ * directly execute the tool without an LLM roundtrip.
20
+ */
21
+ export declare const GenerativeUIWithActions: Story;
@@ -0,0 +1,11 @@
1
+ import { Cassette, ReplayOptions } from '../lib/cassette';
2
+ import { ElementsConfig } from '../types';
3
+ import { ReactNode } from 'react';
4
+ export interface ReplayProps extends ReplayOptions {
5
+ /** The recorded cassette to replay. */
6
+ cassette: Cassette;
7
+ children: ReactNode;
8
+ /** Optional ElementsConfig for visual customization (theme, variant, etc.) */
9
+ config?: Partial<ElementsConfig>;
10
+ }
11
+ export declare const Replay: ({ cassette, children, config: partialConfig, typingSpeed, userMessageDelay, assistantStartDelay, onComplete, }: ReplayProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { StoryFn } from '@storybook/react-vite';
2
+ import { Replay } from './Replay';
3
+ declare const meta: any;
4
+ export default meta;
5
+ type Story = StoryFn<typeof Replay>;
6
+ /**
7
+ * Basic text conversation replay. Messages stream in character-by-character
8
+ * with a typing effect, just like a real chat session.
9
+ */
10
+ export declare const TextConversation: Story;
11
+ /**
12
+ * Replay with tool calls. The assistant invokes tools with visible
13
+ * arguments and results, demonstrating the tool call rendering pipeline.
14
+ */
15
+ export declare const ToolCalls: Story;
16
+ /**
17
+ * Replay with reasoning (chain-of-thought) content. The assistant's
18
+ * internal reasoning is shown before the final response.
19
+ */
20
+ export declare const Reasoning: Story;
21
+ /**
22
+ * Multi-turn conversation with multiple user/assistant exchanges,
23
+ * demonstrating extended conversations with rich markdown formatting.
24
+ */
25
+ export declare const MultiTurn: Story;
26
+ /**
27
+ * Replay rendered in the widget (modal) variant, showing how replays
28
+ * work inside a modal container.
29
+ */
30
+ export declare const WidgetVariant: Story;
31
+ /**
32
+ * Fast replay with minimal delays — useful for quick demos or testing.
33
+ */
34
+ export declare const FastReplay: Story;
@@ -0,0 +1,16 @@
1
+ import { FC } from 'react';
2
+ interface ConnectionStatusIndicatorProps {
3
+ className?: string;
4
+ }
5
+ /**
6
+ * iOS-style floating island that appears when connection is lost.
7
+ * Shows "Reconnecting..." with a spinner, or "Connection Lost" after timeout.
8
+ */
9
+ export declare const ConnectionStatusIndicator: FC<ConnectionStatusIndicatorProps>;
10
+ /**
11
+ * Wrapper version that handles the case when ConnectionStatusProvider is not available.
12
+ * Uses useConnectionStatusOptional() which returns null instead of throwing,
13
+ * since try-catch around JSX won't catch hook errors (they throw during render phase).
14
+ */
15
+ export declare const ConnectionStatusIndicatorSafe: FC<ConnectionStatusIndicatorProps>;
16
+ export {};
@@ -0,0 +1,6 @@
1
+ import { FC } from 'react';
2
+ /**
3
+ * Displays follow-on suggestions after the assistant finishes responding.
4
+ * These are dynamically generated based on the conversation context.
5
+ */
6
+ export declare const FollowOnSuggestions: FC;
@@ -0,0 +1,9 @@
1
+ import { FC } from 'react';
2
+ export type FeedbackType = 'dislike' | 'like';
3
+ interface MessageFeedbackProps {
4
+ onFeedback?: (type: FeedbackType) => void;
5
+ onResolved?: () => void;
6
+ className?: string;
7
+ }
8
+ export declare const MessageFeedback: FC<MessageFeedbackProps>;
9
+ export {};
@@ -1,7 +1,7 @@
1
1
  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
- variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
4
+ variant?: "default" | "link" | "secondary" | "outline" | "destructive" | "ghost" | null | undefined;
5
5
  size?: "default" | "icon" | "sm" | "lg" | "icon-sm" | "icon-lg" | null | undefined;
6
6
  } & import('class-variance-authority/types').ClassProp) | undefined) => string> & {
7
7
  asChild?: boolean;
@@ -1,4 +1,4 @@
1
1
  export declare const buttonVariants: (props?: ({
2
- variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
2
+ variant?: "default" | "link" | "secondary" | "outline" | "destructive" | "ghost" | null | undefined;
3
3
  size?: "default" | "icon" | "sm" | "lg" | "icon-sm" | "icon-lg" | null | undefined;
4
4
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
@@ -0,0 +1,13 @@
1
+ import { FC } from 'react';
2
+ interface GenerativeUIProps {
3
+ /** The JSON content to render - can be a json-render tree or raw object */
4
+ content: unknown;
5
+ /** Additional class names */
6
+ className?: string;
7
+ }
8
+ /**
9
+ * GenerativeUI component renders json-render compatible JSON as dynamic UI widgets.
10
+ * This is used by the generativeUI plugin to render `ui` code blocks.
11
+ */
12
+ export declare const GenerativeUI: FC<GenerativeUIProps>;
13
+ export type { GenerativeUIProps };
@@ -0,0 +1,27 @@
1
+ import { ReactNode } from 'react';
2
+ export type ConnectionState = 'connected' | 'reconnecting' | 'disconnected';
3
+ interface ConnectionStatusContextValue {
4
+ /** Current connection state */
5
+ state: ConnectionState;
6
+ /** Number of reconnection attempts */
7
+ retryCount: number;
8
+ /** Whether the browser reports being online */
9
+ isOnline: boolean;
10
+ /** Mark connection as failed - will trigger reconnecting state */
11
+ markDisconnected: () => void;
12
+ /** Mark connection as restored */
13
+ markConnected: () => void;
14
+ /** Reset the connection state */
15
+ reset: () => void;
16
+ }
17
+ interface ConnectionStatusProviderProps {
18
+ children: ReactNode;
19
+ }
20
+ export declare const ConnectionStatusProvider: ({ children, }: ConnectionStatusProviderProps) => import("react/jsx-runtime").JSX.Element;
21
+ export declare const useConnectionStatus: () => ConnectionStatusContextValue;
22
+ /**
23
+ * Hook that returns connection status helpers for use in sendMessages.
24
+ * Returns null if not within a ConnectionStatusProvider (for backwards compatibility).
25
+ */
26
+ export declare const useConnectionStatusOptional: () => ConnectionStatusContextValue | null;
27
+ export {};
@@ -0,0 +1,6 @@
1
+ export declare const ReplayContext: import('react').Context<{
2
+ isReplay: boolean;
3
+ } | null>;
4
+ export declare function useReplayContext(): {
5
+ isReplay: boolean;
6
+ } | null;
@@ -0,0 +1,21 @@
1
+ import { ReactNode } from 'react';
2
+ interface ExecutableTool {
3
+ execute?: (args: unknown, options?: unknown) => Promise<unknown>;
4
+ }
5
+ type ExecutableToolSet = Record<string, ExecutableTool | undefined>;
6
+ export interface ToolExecutionResult {
7
+ success: boolean;
8
+ result?: unknown;
9
+ error?: string;
10
+ }
11
+ interface ToolExecutionContextValue {
12
+ executeTool: (toolName: string, args: Record<string, unknown>) => Promise<ToolExecutionResult>;
13
+ isToolAvailable: (toolName: string) => boolean;
14
+ }
15
+ interface ToolExecutionProviderProps {
16
+ children: ReactNode;
17
+ tools: ExecutableToolSet | undefined;
18
+ }
19
+ export declare function ToolExecutionProvider({ children, tools, }: ToolExecutionProviderProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function useToolExecution(): ToolExecutionContextValue;
21
+ export {};
package/dist/elements.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-Dip7A_UI.cjs"),r=require("./index-CNVoovK7.cjs");exports.Chat=e.Chat;exports.ChatHistory=e.ChatHistory;exports.ElementsProvider=e.ElementsProvider;exports.GramElementsProvider=e.ElementsProvider;exports.MODELS=e.MODELS;exports.ShareButton=e.ShareButton;exports.defineFrontendTool=e.defineFrontendTool;exports.trackError=e.trackError;exports.useThreadId=e.useThreadId;exports.useElements=r.useElements;exports.useGramElements=r.useElements;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-Bps9R2k6.cjs"),r=require("./index-iUSSoKFz.cjs");exports.Chat=e.Chat;exports.ChatHistory=e.ChatHistory;exports.ElementsProvider=e.ElementsProvider;exports.GramElementsProvider=e.ElementsProvider;exports.MODELS=e.MODELS;exports.Replay=e.Replay;exports.ShareButton=e.ShareButton;exports.defineFrontendTool=e.defineFrontendTool;exports.trackError=e.trackError;exports.useRecordCassette=e.useRecordCassette;exports.useThreadId=e.useThreadId;exports.useElements=r.useElements;exports.useGramElements=r.useElements;
2
2
  //# sourceMappingURL=elements.cjs.map