@gram-ai/elements 1.20.2 → 1.21.2

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 (74) hide show
  1. package/bin/cli.js +14 -12
  2. package/dist/components/Chat/stories/ConnectionConfiguration.stories.d.ts +2 -2
  3. package/dist/components/Chat/stories/ToolApproval.stories.d.ts +2 -0
  4. package/dist/components/ui/dialog.d.ts +1 -1
  5. package/dist/components/ui/tooltip.d.ts +3 -1
  6. package/dist/constants/tailwind.d.ts +1 -0
  7. package/dist/contexts/portal-container-context.d.ts +2 -0
  8. package/dist/contexts/portal-container.d.ts +7 -0
  9. package/dist/elements.cjs +1 -160
  10. package/dist/elements.cjs.map +1 -1
  11. package/dist/elements.css +1 -1
  12. package/dist/elements.js +11 -47215
  13. package/dist/elements.js.map +1 -1
  14. package/dist/hooks/usePortalContainer.d.ts +8 -0
  15. package/dist/hooks/useSession.d.ts +1 -2
  16. package/dist/index-BVvrv2G3.cjs +169 -0
  17. package/dist/index-BVvrv2G3.cjs.map +1 -0
  18. package/dist/index-OU3wjArm.js +54670 -0
  19. package/dist/index-OU3wjArm.js.map +1 -0
  20. package/dist/index.d.ts +3 -1
  21. package/dist/lib/auth.d.ts +2 -2
  22. package/dist/lib/errorTracking.config.d.ts +16 -0
  23. package/dist/lib/errorTracking.d.ts +24 -0
  24. package/dist/lib/tools.d.ts +3 -2
  25. package/dist/profiler-DPH9zydw.cjs +2 -0
  26. package/dist/profiler-DPH9zydw.cjs.map +1 -0
  27. package/dist/profiler-D_HNXmyv.js +278 -0
  28. package/dist/profiler-D_HNXmyv.js.map +1 -0
  29. package/dist/startRecording-B97e4Mlu.cjs +3 -0
  30. package/dist/startRecording-B97e4Mlu.cjs.map +1 -0
  31. package/dist/startRecording-DOMzQAAr.js +1212 -0
  32. package/dist/startRecording-DOMzQAAr.js.map +1 -0
  33. package/dist/types/index.d.ts +45 -15
  34. package/package.json +16 -2
  35. package/src/components/Chat/index.tsx +31 -3
  36. package/src/components/Chat/stories/Composer.stories.tsx +0 -7
  37. package/src/components/Chat/stories/ConnectionConfiguration.stories.tsx +7 -14
  38. package/src/components/Chat/stories/CustomComponents.stories.tsx +0 -7
  39. package/src/components/Chat/stories/Density.stories.tsx +0 -7
  40. package/src/components/Chat/stories/ErrorBoundary.stories.tsx +4 -23
  41. package/src/components/Chat/stories/FrontendTools.stories.tsx +0 -7
  42. package/src/components/Chat/stories/Model.stories.tsx +0 -7
  43. package/src/components/Chat/stories/Plugins.stories.tsx +0 -7
  44. package/src/components/Chat/stories/Radius.stories.tsx +0 -7
  45. package/src/components/Chat/stories/ToolApproval.stories.tsx +51 -7
  46. package/src/components/Chat/stories/Tools.stories.tsx +0 -7
  47. package/src/components/Chat/stories/Variants.stories.tsx +5 -2
  48. package/src/components/Chat/stories/Welcome.stories.tsx +0 -8
  49. package/src/components/assistant-ui/assistant-sidecar.tsx +1 -4
  50. package/src/components/assistant-ui/attachment.tsx +1 -4
  51. package/src/components/assistant-ui/error-boundary.tsx +6 -0
  52. package/src/components/assistant-ui/thread-list.tsx +3 -1
  53. package/src/components/assistant-ui/thread.tsx +10 -12
  54. package/src/components/ui/dialog.tsx +10 -1
  55. package/src/components/ui/popover.tsx +10 -12
  56. package/src/components/ui/tooltip.tsx +7 -2
  57. package/src/constants/tailwind.ts +2 -0
  58. package/src/contexts/ElementsProvider.tsx +13 -1
  59. package/src/contexts/portal-container-context.ts +4 -0
  60. package/src/contexts/portal-container.tsx +20 -0
  61. package/src/global.css +129 -16
  62. package/src/hooks/useAuth.ts +6 -16
  63. package/src/hooks/usePortalContainer.ts +16 -0
  64. package/src/hooks/useSession.ts +1 -3
  65. package/src/index.ts +5 -0
  66. package/src/lib/api.test.ts +5 -5
  67. package/src/lib/auth.ts +4 -4
  68. package/src/lib/errorTracking.config.ts +16 -0
  69. package/src/lib/errorTracking.ts +104 -0
  70. package/src/lib/tools.ts +37 -8
  71. package/src/types/index.ts +48 -16
  72. package/src/vite-env.d.ts +3 -0
  73. package/dist/components/Chat/stories/ColorScheme.stories.d.ts +0 -8
  74. package/src/components/Chat/stories/ColorScheme.stories.tsx +0 -52
@@ -10,13 +10,6 @@ const meta: Meta<typeof Chat> = {
10
10
  parameters: {
11
11
  layout: 'fullscreen',
12
12
  },
13
- decorators: [
14
- (Story) => (
15
- <div className="m-auto flex h-screen w-full max-w-3xl flex-col">
16
- <Story />
17
- </div>
18
- ),
19
- ],
20
13
  } satisfies Meta<typeof Chat>
21
14
 
22
15
  export default meta
@@ -44,6 +37,30 @@ SingleTool.parameters = {
44
37
  },
45
38
  }
46
39
 
40
+ export const SingleToolWithFunction: Story = () => <Chat />
41
+ SingleToolWithFunction.storyName =
42
+ 'Single Tool Requiring Approval with Function'
43
+ SingleToolWithFunction.parameters = {
44
+ elements: {
45
+ config: {
46
+ variant: 'standalone',
47
+ tools: {
48
+ toolsRequiringApproval: ({ toolName }: { toolName: string }) =>
49
+ toolName.endsWith('salutation'),
50
+ },
51
+ welcome: {
52
+ suggestions: [
53
+ {
54
+ title: 'Call a tool requiring approval',
55
+ label: 'Get a salutation',
56
+ action: 'Get a salutation',
57
+ },
58
+ ],
59
+ },
60
+ },
61
+ },
62
+ }
63
+
47
64
  export const MultipleGroupedTools: Story = () => <Chat />
48
65
  MultipleGroupedTools.storyName = 'Multiple Grouped Tools'
49
66
  MultipleGroupedTools.parameters = {
@@ -108,3 +125,30 @@ FrontendTool.parameters = {
108
125
  },
109
126
  },
110
127
  }
128
+
129
+ export const FrontendToolWithFunction: Story = () => <Chat />
130
+ FrontendToolWithFunction.storyName =
131
+ 'Frontend Tool Requiring Approval with Function'
132
+ FrontendToolWithFunction.parameters = {
133
+ elements: {
134
+ config: {
135
+ variant: 'standalone',
136
+ tools: {
137
+ frontendTools: {
138
+ deleteFile,
139
+ },
140
+ toolsRequiringApproval: ({ toolName }: { toolName: string }) =>
141
+ toolName.startsWith('delete'),
142
+ },
143
+ welcome: {
144
+ suggestions: [
145
+ {
146
+ title: 'Delete a file',
147
+ label: 'Delete a file',
148
+ action: 'Delete file with ID 123',
149
+ },
150
+ ],
151
+ },
152
+ },
153
+ },
154
+ }
@@ -9,13 +9,6 @@ const meta: Meta<typeof Chat> = {
9
9
  parameters: {
10
10
  layout: 'fullscreen',
11
11
  },
12
- decorators: [
13
- (Story) => (
14
- <div className="m-auto flex h-screen w-full max-w-3xl flex-col">
15
- <Story />
16
- </div>
17
- ),
18
- ],
19
12
  } satisfies Meta<typeof Chat>
20
13
 
21
14
  export default meta
@@ -1,6 +1,7 @@
1
1
  import { Chat } from '..'
2
2
  import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite'
3
3
  import { ThreadList } from '@/components/assistant-ui/thread-list'
4
+ import { ROOT_SELECTOR } from '@/constants/tailwind'
4
5
 
5
6
  const meta: Meta<typeof Chat> = {
6
7
  title: 'Chat/Variants',
@@ -46,8 +47,10 @@ export const StandaloneWithHistory: StoryObj<typeof Chat> = {
46
47
  }
47
48
  StandaloneWithHistory.decorators = [
48
49
  (Story) => (
49
- <div className="m-auto flex h-screen w-full items-center justify-center border bg-linear-to-r from-violet-600 to-indigo-800">
50
- <Story />
50
+ <div className={ROOT_SELECTOR}>
51
+ <div className="m-auto flex h-screen w-full items-center justify-center border bg-linear-to-r from-violet-600 to-indigo-800">
52
+ <Story />
53
+ </div>
51
54
  </div>
52
55
  ),
53
56
  ]
@@ -1,4 +1,3 @@
1
- import React from 'react'
2
1
  import { Chat } from '..'
3
2
  import type { Meta, StoryFn } from '@storybook/react-vite'
4
3
 
@@ -8,13 +7,6 @@ const meta: Meta<typeof Chat> = {
8
7
  parameters: {
9
8
  layout: 'fullscreen',
10
9
  },
11
- decorators: [
12
- (Story) => (
13
- <div className="m-auto flex h-screen w-full max-w-3xl flex-col">
14
- <Story />
15
- </div>
16
- ),
17
- ],
18
10
  } satisfies Meta<typeof Chat>
19
11
 
20
12
  export default meta
@@ -37,10 +37,7 @@ export const AssistantSidecar: FC<AssistantSidecarProps> = ({ className }) => {
37
37
  return (
38
38
  <LazyMotion features={domMax}>
39
39
  <m.div
40
- initial={{
41
- width: dimensions?.default?.width ?? '400px',
42
- height: dimensions?.default?.height ?? '100vh',
43
- }}
40
+ initial={false}
44
41
  animate={{
45
42
  width: isExpanded
46
43
  ? (dimensions?.expanded?.width ?? '800px')
@@ -87,10 +87,7 @@ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
87
87
 
88
88
  return (
89
89
  <Dialog>
90
- <DialogTrigger
91
- className="aui-attachment-preview-trigger hover:bg-accent/50 cursor-pointer transition-colors"
92
- asChild
93
- >
90
+ <DialogTrigger className="aui-attachment-preview-trigger" asChild>
94
91
  {children}
95
92
  </DialogTrigger>
96
93
  <DialogContent className="aui-attachment-preview-dialog-content [&_svg]:text-background [&>button]:bg-foreground/60 [&>button]:hover:[&_svg]:text-destructive p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:p-1 [&>button]:opacity-100 [&>button]:!ring-0">
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { AlertCircle } from 'lucide-react'
4
4
  import { Component, type ErrorInfo, type ReactNode } from 'react'
5
+ import { trackError } from '@/lib/errorTracking'
5
6
  import { cn } from '@/lib/utils'
6
7
  import { Button } from '../ui/button'
7
8
 
@@ -81,6 +82,11 @@ export class ErrorBoundary extends Component<
81
82
  }
82
83
 
83
84
  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
85
+ // Track error to Datadog RUM
86
+ trackError(error, {
87
+ source: 'error-boundary',
88
+ componentStack: errorInfo.componentStack ?? undefined,
89
+ })
84
90
  this.props.onError?.(error, errorInfo)
85
91
  }
86
92
 
@@ -11,6 +11,7 @@ import { Skeleton } from '@/components/ui/skeleton'
11
11
  import { useRadius } from '@/hooks/useRadius'
12
12
  import { cn } from '@/lib/utils'
13
13
  import { useDensity } from '@/hooks/useDensity'
14
+ import { ROOT_SELECTOR } from '@/constants/tailwind'
14
15
 
15
16
  interface ThreadListProps {
16
17
  className?: string
@@ -23,7 +24,8 @@ export const ThreadList: FC<ThreadListProps> = ({ className }) => {
23
24
  className={cn(
24
25
  'aui-root aui-thread-list-root bg-background flex flex-col items-stretch',
25
26
  d('gap-sm'),
26
- className
27
+ className,
28
+ ROOT_SELECTOR
27
29
  )}
28
30
  >
29
31
  <div
@@ -6,7 +6,6 @@ import {
6
6
  ChevronRightIcon,
7
7
  CopyIcon,
8
8
  PencilIcon,
9
- RefreshCwIcon,
10
9
  Settings2,
11
10
  Square,
12
11
  } from 'lucide-react'
@@ -52,19 +51,18 @@ import {
52
51
  } from '../ui/tooltip'
53
52
  import { ToolGroup } from './tool-group'
54
53
 
55
- const ApiKeyWarning = () => (
54
+ const StaticSessionWarning = () => (
56
55
  <div className="m-2 rounded-md border border-amber-500 bg-amber-100 px-4 py-3 text-sm text-amber-800 dark:border-amber-600 dark:bg-amber-900/30 dark:text-amber-200">
57
- <strong>Warning:</strong> You are using an API key directly in the client.
58
- Please{' '}
56
+ <strong>Warning:</strong> You are using a static session token in the
57
+ client. It will expire shortly. Please{' '}
59
58
  <a
60
59
  href="https://github.com/speakeasy-api/gram/tree/main/elements#setting-up-your-backend"
61
60
  target="_blank"
62
61
  rel="noopener noreferrer"
63
62
  className="text-amber-700 underline hover:text-amber-800 dark:text-amber-300 dark:hover:text-amber-200"
64
63
  >
65
- set up a session endpoint
66
- </a>{' '}
67
- before deploying to production.
64
+ set up a session endpoint to avoid this warning.
65
+ </a>
68
66
  </div>
69
67
  )
70
68
 
@@ -77,7 +75,7 @@ export const Thread: FC<ThreadProps> = ({ className }) => {
77
75
  const d = useDensity()
78
76
  const { config } = useElements()
79
77
  const components = config.components ?? {}
80
- const showApiKeyWarning = config.api && 'UNSAFE_apiKey' in config.api
78
+ const showStaticSessionWarning = config.api && 'sessionToken' in config.api
81
79
 
82
80
  return (
83
81
  <LazyMotion features={domAnimation}>
@@ -103,7 +101,7 @@ export const Thread: FC<ThreadProps> = ({ className }) => {
103
101
  )}
104
102
  </ThreadPrimitive.If>
105
103
 
106
- {showApiKeyWarning && <ApiKeyWarning />}
104
+ {showStaticSessionWarning && <StaticSessionWarning />}
107
105
 
108
106
  <ThreadPrimitive.Messages
109
107
  components={{
@@ -495,7 +493,7 @@ const AssistantMessage: FC = () => {
495
493
  </div>
496
494
 
497
495
  <div className="aui-assistant-message-footer mt-2 ml-2 flex">
498
- <BranchPicker />
496
+ {/* <BranchPicker /> */}
499
497
  <AssistantActionBar />
500
498
  </div>
501
499
  </div>
@@ -525,11 +523,11 @@ const AssistantActionBar: FC = () => {
525
523
  </MessagePrimitive.If>
526
524
  </TooltipIconButton>
527
525
  </ActionBarPrimitive.Copy>
528
- <ActionBarPrimitive.Reload asChild>
526
+ {/* <ActionBarPrimitive.Reload asChild>
529
527
  <TooltipIconButton tooltip="Refresh">
530
528
  <RefreshCwIcon />
531
529
  </TooltipIconButton>
532
- </ActionBarPrimitive.Reload>
530
+ </ActionBarPrimitive.Reload> */}
533
531
  </ActionBarPrimitive.Root>
534
532
  )
535
533
  }
@@ -3,6 +3,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog'
3
3
  import { XIcon } from 'lucide-react'
4
4
 
5
5
  import { cn } from '@/lib/utils'
6
+ import { usePortalContainer } from '@/hooks/usePortalContainer'
6
7
 
7
8
  function Dialog({
8
9
  ...props
@@ -17,9 +18,17 @@ function DialogTrigger({
17
18
  }
18
19
 
19
20
  function DialogPortal({
21
+ container,
20
22
  ...props
21
23
  }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
22
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
24
+ const portalContainer = usePortalContainer()
25
+ return (
26
+ <DialogPrimitive.Portal
27
+ data-slot="dialog-portal"
28
+ container={container ?? portalContainer}
29
+ {...props}
30
+ />
31
+ )
23
32
  }
24
33
 
25
34
  function DialogClose({
@@ -22,18 +22,16 @@ function PopoverContent({
22
22
  ...props
23
23
  }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
24
24
  return (
25
- <PopoverPrimitive.Portal>
26
- <PopoverPrimitive.Content
27
- data-slot="popover-content"
28
- align={align}
29
- sideOffset={sideOffset}
30
- className={cn(
31
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-20 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden',
32
- className
33
- )}
34
- {...props}
35
- />
36
- </PopoverPrimitive.Portal>
25
+ <PopoverPrimitive.Content
26
+ data-slot="popover-content"
27
+ align={align}
28
+ sideOffset={sideOffset}
29
+ className={cn(
30
+ 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-20 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden',
31
+ className
32
+ )}
33
+ {...props}
34
+ />
37
35
  )
38
36
  }
39
37
 
@@ -4,6 +4,7 @@ import * as React from 'react'
4
4
  import * as TooltipPrimitive from '@radix-ui/react-tooltip'
5
5
 
6
6
  import { cn } from '@/lib/utils'
7
+ import { usePortalContainer } from '@/hooks/usePortalContainer'
7
8
 
8
9
  function TooltipProvider({
9
10
  delayDuration = 0,
@@ -38,10 +39,14 @@ function TooltipContent({
38
39
  className,
39
40
  sideOffset = 0,
40
41
  children,
42
+ container,
41
43
  ...props
42
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
44
+ }: React.ComponentProps<typeof TooltipPrimitive.Content> & {
45
+ container?: HTMLElement | null
46
+ }) {
47
+ const portalContainer = usePortalContainer()
43
48
  return (
44
- <TooltipPrimitive.Portal>
49
+ <TooltipPrimitive.Portal container={container ?? portalContainer}>
45
50
  <TooltipPrimitive.Content
46
51
  data-slot="tooltip-content"
47
52
  sideOffset={sideOffset}
@@ -0,0 +1,2 @@
1
+ // Used to scope elements styles to the Elements library
2
+ export const ROOT_SELECTOR = 'gram-elements'
@@ -2,6 +2,7 @@ import { FrontendTools } from '@/components/FrontendTools'
2
2
  import { useMCPTools } from '@/hooks/useMCPTools'
3
3
  import { useToolApproval } from '@/hooks/useToolApproval'
4
4
  import { getApiUrl } from '@/lib/api'
5
+ import { initErrorTracking, trackError } from '@/lib/errorTracking'
5
6
  import { MODELS } from '@/lib/models'
6
7
  import {
7
8
  clearFrontendToolApprovalConfig,
@@ -125,6 +126,15 @@ const ElementsProviderWithApproval = ({
125
126
  plugins
126
127
  )
127
128
 
129
+ // Initialize error tracking on mount
130
+ useEffect(() => {
131
+ initErrorTracking({
132
+ enabled: config.errorTracking?.enabled,
133
+ projectSlug: config.projectSlug,
134
+ variant: config.variant,
135
+ })
136
+ }, [])
137
+
128
138
  const { data: mcpTools } = useMCPTools({
129
139
  auth,
130
140
  mcp: config.mcp,
@@ -157,7 +167,7 @@ const ElementsProviderWithApproval = ({
157
167
 
158
168
  // Set up frontend tool approval config for runtime checking
159
169
  useEffect(() => {
160
- if (config.tools?.toolsRequiringApproval?.length) {
170
+ if (config.tools?.toolsRequiringApproval) {
161
171
  setFrontendToolApprovalConfig(
162
172
  getApprovalHelpers(),
163
173
  config.tools.toolsRequiringApproval
@@ -249,12 +259,14 @@ const ElementsProviderWithApproval = ({
249
259
  abortSignal,
250
260
  onError: ({ error }) => {
251
261
  console.error('Stream error in onError callback:', error)
262
+ trackError(error, { source: 'streaming' })
252
263
  },
253
264
  })
254
265
 
255
266
  return result.toUIMessageStream()
256
267
  } catch (error) {
257
268
  console.error('Error creating stream:', error)
269
+ trackError(error, { source: 'stream-creation' })
258
270
  throw error
259
271
  }
260
272
  },
@@ -0,0 +1,4 @@
1
+ import { createContext, type RefObject } from 'react'
2
+
3
+ export const PortalContainerContext =
4
+ createContext<RefObject<HTMLElement | null> | null>(null)
@@ -0,0 +1,20 @@
1
+ 'use client'
2
+
3
+ import { type RefObject } from 'react'
4
+ import { PortalContainerContext } from './portal-container-context'
5
+
6
+ export { PortalContainerContext }
7
+
8
+ export function PortalContainerProvider({
9
+ containerRef,
10
+ children,
11
+ }: {
12
+ containerRef: RefObject<HTMLElement | null>
13
+ children: React.ReactNode
14
+ }) {
15
+ return (
16
+ <PortalContainerContext.Provider value={containerRef}>
17
+ {children}
18
+ </PortalContainerContext.Provider>
19
+ )
20
+ }
package/src/global.css CHANGED
@@ -1,6 +1,7 @@
1
- @import "tailwindcss";
2
- @import "tw-animate-css";
3
- @import "tw-shimmer";
1
+ @import 'tw-animate-css';
2
+ @import 'tw-shimmer';
3
+ @layer theme, base, components, utilities;
4
+ @import 'tailwindcss/theme.css' layer(theme);
4
5
 
5
6
  @custom-variant dark (&:is(.dark *));
6
7
 
@@ -42,7 +43,20 @@
42
43
  --color-sidebar-ring: var(--sidebar-ring);
43
44
  }
44
45
 
45
- :root {
46
+ /* Scope ALL variables to .gram-elements instead of :root */
47
+ .gram-elements {
48
+ /* Scoped preflight/base styles */
49
+ font-family:
50
+ ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
51
+ 'Segoe UI Symbol', 'Noto Color Emoji';
52
+ font-feature-settings: normal;
53
+ font-variation-settings: normal;
54
+ -webkit-font-smoothing: antialiased;
55
+ -moz-osx-font-smoothing: grayscale;
56
+ line-height: 1.5;
57
+ -webkit-tap-highlight-color: transparent;
58
+ tab-size: 4;
59
+
46
60
  /* Theme: Radius - set via data-radius attribute */
47
61
  --radius: 0.625rem;
48
62
  --background: oklch(1 0 0);
@@ -78,7 +92,9 @@
78
92
  --sidebar-ring: oklch(0.705 0.015 286.067);
79
93
  }
80
94
 
81
- .dark {
95
+ /* Dark mode scoped to .gram-elements */
96
+ .gram-elements.dark,
97
+ .dark .gram-elements {
82
98
  --background: oklch(0.141 0.005 285.823);
83
99
  --foreground: oklch(0.985 0 0);
84
100
  --card: oklch(0.21 0.006 285.885);
@@ -113,24 +129,121 @@
113
129
  }
114
130
 
115
131
  @layer base {
116
- * {
117
- @apply border-border outline-ring/50;
118
- }
119
- body {
120
- @apply bg-background text-foreground;
132
+ .gram-elements {
133
+ @tailwind utilities;
134
+
135
+ /* We can't use tailwind's preflight styles here because we need to scope them to .gram-elements so therefore we just include a version of them inline here */
136
+ *,
137
+ *::before,
138
+ *::after {
139
+ box-sizing: border-box;
140
+ border-width: 0;
141
+ border-style: solid;
142
+ @apply border-border outline-ring/50;
143
+ }
144
+
145
+ /* Reset margins and padding for common elements */
146
+ h1,
147
+ h2,
148
+ h3,
149
+ h4,
150
+ h5,
151
+ h6,
152
+ p,
153
+ blockquote,
154
+ pre,
155
+ ul,
156
+ ol,
157
+ figure {
158
+ margin: 0;
159
+ padding: 0;
160
+ }
161
+
162
+ /* List style reset */
163
+ ul,
164
+ ol {
165
+ list-style: none;
166
+ }
167
+
168
+ /* Image defaults */
169
+ img,
170
+ svg,
171
+ video,
172
+ canvas,
173
+ audio,
174
+ iframe,
175
+ embed,
176
+ object {
177
+ display: block;
178
+ }
179
+
180
+ img,
181
+ video {
182
+ max-width: 100%;
183
+ height: auto;
184
+ }
185
+
186
+ /* Button/input reset */
187
+ button,
188
+ input,
189
+ optgroup,
190
+ select,
191
+ textarea {
192
+ font-family: inherit;
193
+ font-feature-settings: inherit;
194
+ font-variation-settings: inherit;
195
+ font-size: 100%;
196
+ font-weight: inherit;
197
+ line-height: inherit;
198
+ letter-spacing: inherit;
199
+ color: inherit;
200
+ margin: 0;
201
+ padding: 0;
202
+ }
203
+
204
+ button,
205
+ select {
206
+ text-transform: none;
207
+ }
208
+
209
+ button,
210
+ input:where([type='button']),
211
+ input:where([type='reset']),
212
+ input:where([type='submit']) {
213
+ appearance: button;
214
+ -webkit-appearance: button;
215
+ background-color: transparent;
216
+ background-image: none;
217
+ }
218
+
219
+ a {
220
+ color: inherit;
221
+ text-decoration: inherit;
222
+ }
223
+
224
+ /* Table reset */
225
+ table {
226
+ text-indent: 0;
227
+ border-color: inherit;
228
+ border-collapse: collapse;
229
+ }
121
230
  }
122
231
  }
123
232
 
124
- /* Theme: Radius variants via data attribute */
125
- [data-radius='sharp'] {
233
+ /* Theme: Radius variants via data attribute - scoped */
234
+ .gram-elements[data-radius='sharp'],
235
+ [data-radius='sharp'] .gram-elements {
126
236
  --radius: 0.25rem;
127
237
  }
128
- [data-radius='soft'] {
238
+ .gram-elements[data-radius='soft'],
239
+ [data-radius='soft'] .gram-elements {
129
240
  --radius: 0.625rem;
130
241
  }
131
- [data-radius='round'] {
242
+ .gram-elements[data-radius='round'],
243
+ [data-radius='round'] .gram-elements {
132
244
  --radius: 1rem;
133
245
  }
134
- [data-radius='pill'] {
246
+ .gram-elements[data-radius='pill'],
247
+ [data-radius='pill'] .gram-elements {
135
248
  --radius: 9999px;
136
- }
249
+ }
@@ -1,7 +1,7 @@
1
- import { hasExplicitSessionAuth, isApiKeyAuth } from '@/lib/auth'
1
+ import { hasExplicitSessionAuth, isStaticSessionAuth } from '@/lib/auth'
2
+ import { useMemo } from 'react'
2
3
  import { ApiConfig } from '../types'
3
4
  import { useSession } from './useSession'
4
- import { useMemo } from 'react'
5
5
 
6
6
  export type Auth =
7
7
  | {
@@ -38,33 +38,23 @@ export const useAuth = ({
38
38
  projectSlug: string
39
39
  }): Auth => {
40
40
  const getSession = useMemo(() => {
41
- if (isApiKeyAuth(auth)) {
42
- return null
41
+ if (isStaticSessionAuth(auth)) {
42
+ return () => Promise.resolve(auth.sessionToken)
43
43
  }
44
- return !isApiKeyAuth(auth) && hasExplicitSessionAuth(auth)
44
+ return !isStaticSessionAuth(auth) && hasExplicitSessionAuth(auth)
45
45
  ? auth.sessionFn
46
46
  : defaultGetSession
47
47
  }, [auth])
48
+
48
49
  // The session request is only neccessary if we are not using an API key auth
49
50
  // configuration. If a custom session fetcher is provided, we use it,
50
51
  // otherwise we fallback to the default session fetcher
51
52
  const session = useSession({
52
53
  // We want to check it's NOT API key auth, as the default auth scheme is session auth (if the user hasn't provided an explicit API config, we have a session auth config by default)
53
- enabled: !isApiKeyAuth(auth),
54
54
  getSession,
55
55
  projectSlug,
56
56
  })
57
57
 
58
- if (isApiKeyAuth(auth)) {
59
- return {
60
- headers: {
61
- 'Gram-Project': projectSlug,
62
- 'Gram-Key': auth.UNSAFE_apiKey,
63
- },
64
- isLoading: false,
65
- }
66
- }
67
-
68
58
  return !session
69
59
  ? {
70
60
  isLoading: true,
@@ -0,0 +1,16 @@
1
+ 'use client'
2
+
3
+ import { useContext } from 'react'
4
+ import { PortalContainerContext } from '@/contexts/portal-container-context'
5
+
6
+ /**
7
+ * Because we do not want Tailwind to leak from the Elements library, and
8
+ * because some UI elements such as Dialogs and Tooltips need to be rendered in
9
+ * a different container than the root element, we need to use a portal
10
+ * container, which renders any tooltips, dialogs etc within the .gram-elements
11
+ * scope so that they still inherit the Elements CSS
12
+ */
13
+ export function usePortalContainer(): HTMLElement | null {
14
+ const ref = useContext(PortalContainerContext)
15
+ return ref?.current ?? null
16
+ }