@gram-ai/elements 1.36.0 → 1.37.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.
Files changed (32) hide show
  1. package/dist/components/assistant-ui/markdown-text.d.ts +1 -1
  2. package/dist/contexts/ThreadMetaContext.d.ts +20 -0
  3. package/dist/elements.cjs +1 -1
  4. package/dist/elements.css +1 -1
  5. package/dist/elements.js +1 -1
  6. package/dist/hooks/useGramThreadListAdapter.d.ts +8 -0
  7. package/dist/{index-BziIHO9O.js → index-BdBraSNn.js} +8698 -8583
  8. package/dist/index-BdBraSNn.js.map +1 -0
  9. package/dist/index-Bl5cH0sz.cjs +194 -0
  10. package/dist/index-Bl5cH0sz.cjs.map +1 -0
  11. package/dist/index-Bv-yE4G1.js.map +1 -1
  12. package/dist/index-CGBkMd0d.cjs.map +1 -1
  13. package/dist/{profiler-xLXOPfmj.js → profiler-Cl-9cG3B.js} +2 -2
  14. package/dist/{profiler-xLXOPfmj.js.map → profiler-Cl-9cG3B.js.map} +1 -1
  15. package/dist/{profiler-DuJEf_S6.cjs → profiler-ttCkbP-N.cjs} +2 -2
  16. package/dist/{profiler-DuJEf_S6.cjs.map → profiler-ttCkbP-N.cjs.map} +1 -1
  17. package/dist/{startRecording-qKnXr4lw.js → startRecording-C41DbnxY.js} +2 -2
  18. package/dist/{startRecording-qKnXr4lw.js.map → startRecording-C41DbnxY.js.map} +1 -1
  19. package/dist/{startRecording-C2XF9-Ol.cjs → startRecording-DLCeKyz9.cjs} +2 -2
  20. package/dist/{startRecording-C2XF9-Ol.cjs.map → startRecording-DLCeKyz9.cjs.map} +1 -1
  21. package/dist/types/index.d.ts +8 -0
  22. package/package.json +3 -3
  23. package/src/components/ShadowRoot.tsx +26 -0
  24. package/src/components/assistant-ui/thinking-indicator.tsx +42 -0
  25. package/src/components/assistant-ui/thread-list.tsx +50 -5
  26. package/src/contexts/ElementsProvider.tsx +31 -5
  27. package/src/contexts/ThreadMetaContext.ts +27 -0
  28. package/src/hooks/useGramThreadListAdapter.tsx +67 -12
  29. package/src/types/index.ts +9 -0
  30. package/dist/index-7fTI_vaV.cjs +0 -194
  31. package/dist/index-7fTI_vaV.cjs.map +0 -1
  32. package/dist/index-BziIHO9O.js.map +0 -1
@@ -15,6 +15,10 @@ import {
15
15
  convertGramMessagesToUIMessages,
16
16
  } from "@/lib/messageConverter";
17
17
  import { sleep } from "@/lib/utils";
18
+ import {
19
+ ThreadMetaContext,
20
+ type ThreadMeta,
21
+ } from "@/contexts/ThreadMetaContext";
18
22
  import {
19
23
  useCallback,
20
24
  useEffect,
@@ -72,6 +76,14 @@ export type ChatMessageTransform = (
72
76
  export interface ThreadListAdapterOptions {
73
77
  apiUrl: string;
74
78
  headers: Record<string, string>;
79
+ /**
80
+ * Async header resolution that waits for auth to settle (e.g. the session
81
+ * token fetch) before returning. When provided, every adapter request uses
82
+ * this instead of the `headers` snapshot — which lets the runtime mount
83
+ * before auth resolves: the initial `list()` fires immediately on bind and
84
+ * would otherwise go out with incomplete headers.
85
+ */
86
+ getHeaders?: () => Promise<Record<string, string>>;
75
87
  /** Map to translate local thread IDs to UUIDs (shared with transport) */
76
88
  localIdToUuidMap?: Map<string, string>;
77
89
  /**
@@ -100,6 +112,29 @@ interface ListChatsResponse {
100
112
  chats: GramChatOverview[];
101
113
  }
102
114
 
115
+ /**
116
+ * Reads a chat's creation timestamp from the `chat.list` payload. The wire
117
+ * format is snake_case (`created_at`) — the hand-written GramChatOverview type
118
+ * declares camelCase, which never matched the JSON — so check both and return
119
+ * an ISO string, or undefined when absent.
120
+ */
121
+ function readChatCreatedAt(chat: GramChatOverview): string | undefined {
122
+ const raw = chat as unknown as Record<string, unknown>;
123
+ const value = raw["created_at"] ?? raw["createdAt"];
124
+ return typeof value === "string" ? value : undefined;
125
+ }
126
+
127
+ /**
128
+ * Resolves request headers from the live adapter options: the async
129
+ * `getHeaders` (which waits for auth to settle) when provided, otherwise the
130
+ * static `headers` snapshot.
131
+ */
132
+ async function resolveAdapterHeaders(
133
+ options: ThreadListAdapterOptions,
134
+ ): Promise<Record<string, string>> {
135
+ return options.getHeaders ? options.getHeaders() : options.headers;
136
+ }
137
+
103
138
  /**
104
139
  * Thread history adapter that loads messages from Gram API.
105
140
  * Note: We use `as ThreadHistoryAdapter` cast because the withFormat generic
@@ -107,7 +142,7 @@ interface ListChatsResponse {
107
142
  */
108
143
  class GramThreadHistoryAdapter {
109
144
  private apiUrl: string;
110
- private headers: Record<string, string>;
145
+ private getHeaders: () => Promise<Record<string, string>>;
111
146
  private store: AssistantApi;
112
147
  // Read lazily rather than captured: the adapter is constructed once, but the
113
148
  // consumer may swap `transformChatMessage` across renders, so resolve it from
@@ -116,12 +151,12 @@ class GramThreadHistoryAdapter {
116
151
 
117
152
  constructor(
118
153
  apiUrl: string,
119
- headers: Record<string, string>,
154
+ getHeaders: () => Promise<Record<string, string>>,
120
155
  store: AssistantApi,
121
156
  getTransformChatMessage?: () => ChatMessageTransform | undefined,
122
157
  ) {
123
158
  this.apiUrl = apiUrl;
124
- this.headers = headers;
159
+ this.getHeaders = getHeaders;
125
160
  this.store = store;
126
161
  this.getTransformChatMessage = getTransformChatMessage;
127
162
  }
@@ -155,7 +190,7 @@ class GramThreadHistoryAdapter {
155
190
  try {
156
191
  const response = await fetch(
157
192
  `${this.apiUrl}/rpc/chat.load?id=${encodeURIComponent(remoteId)}`,
158
- { headers: this.headers },
193
+ { headers: await this.getHeaders() },
159
194
  );
160
195
 
161
196
  if (!response.ok) {
@@ -189,7 +224,7 @@ class GramThreadHistoryAdapter {
189
224
 
190
225
  const response = await fetch(
191
226
  `${this.apiUrl}/rpc/chat.load?id=${encodeURIComponent(remoteId)}`,
192
- { headers: this.headers },
227
+ { headers: await this.getHeaders() },
193
228
  );
194
229
 
195
230
  if (!response.ok) {
@@ -253,7 +288,7 @@ function useGramThreadHistoryAdapter(
253
288
  () =>
254
289
  new GramThreadHistoryAdapter(
255
290
  optionsRef.current.apiUrl,
256
- optionsRef.current.headers,
291
+ () => resolveAdapterHeaders(optionsRef.current),
257
292
  store,
258
293
  () => optionsRef.current.transformChatMessage,
259
294
  ),
@@ -274,6 +309,14 @@ export function useGramThreadListAdapter(
274
309
  optionsRef.current = options;
275
310
  }, [options]);
276
311
 
312
+ // Side channel for per-chat metadata (creation date) that assistant-ui's
313
+ // RemoteThreadMetadata can't carry. `list()` writes a fresh object into the
314
+ // ref and bumps the counter so the provider passes a new context value and
315
+ // ThreadListItem re-renders with the dates. A ref (not state) holds the data
316
+ // so the stable `list()` closure can write it without a re-created identity.
317
+ const metaRef = useRef<Record<string, ThreadMeta>>({});
318
+ const [, bumpMeta] = useState(0);
319
+
277
320
  // Create stable Provider component using useCallback
278
321
  const unstable_Provider = useCallback(function GramHistoryProvider({
279
322
  children,
@@ -281,9 +324,11 @@ export function useGramThreadListAdapter(
281
324
  const history = useGramThreadHistoryAdapter(optionsRef);
282
325
  const adapters = useMemo(() => ({ history }), [history]);
283
326
  return (
284
- <RuntimeAdapterProvider adapters={adapters}>
285
- {children}
286
- </RuntimeAdapterProvider>
327
+ <ThreadMetaContext.Provider value={metaRef.current}>
328
+ <RuntimeAdapterProvider adapters={adapters}>
329
+ {children}
330
+ </RuntimeAdapterProvider>
331
+ </ThreadMetaContext.Provider>
287
332
  );
288
333
  }, []);
289
334
 
@@ -294,7 +339,8 @@ export function useGramThreadListAdapter(
294
339
 
295
340
  async list() {
296
341
  try {
297
- const { apiUrl, headers, threadListFilters } = optionsRef.current;
342
+ const { apiUrl, threadListFilters } = optionsRef.current;
343
+ const headers = await resolveAdapterHeaders(optionsRef.current);
298
344
  const qs = threadListFilters
299
345
  ? new URLSearchParams(threadListFilters).toString()
300
346
  : "";
@@ -309,6 +355,15 @@ export function useGramThreadListAdapter(
309
355
  }
310
356
 
311
357
  const data = (await response.json()) as ListChatsResponse;
358
+ // Stash creation dates in the side channel before returning the
359
+ // assistant-ui metadata (which can't carry them) — keyed by chat id,
360
+ // the same value used for remoteId/externalId below.
361
+ const nextMeta: Record<string, ThreadMeta> = { ...metaRef.current };
362
+ for (const chat of data.chats) {
363
+ nextMeta[chat.id] = { createdAt: readChatCreatedAt(chat) };
364
+ }
365
+ metaRef.current = nextMeta;
366
+ bumpMeta((n) => n + 1);
312
367
  return {
313
368
  threads: data.chats.map((chat) => ({
314
369
  remoteId: chat.id,
@@ -416,7 +471,7 @@ export function useGramThreadListAdapter(
416
471
  {
417
472
  method: "POST",
418
473
  headers: {
419
- ...optionsRef.current.headers,
474
+ ...(await resolveAdapterHeaders(optionsRef.current)),
420
475
  "Content-Type": "application/json",
421
476
  },
422
477
  body: JSON.stringify({ id: remoteId }),
@@ -457,7 +512,7 @@ export function useGramThreadListAdapter(
457
512
  const response = await fetch(
458
513
  `${optionsRef.current.apiUrl}/rpc/chat.load?id=${encodeURIComponent(threadId)}`,
459
514
  {
460
- headers: optionsRef.current.headers,
515
+ headers: await resolveAdapterHeaders(optionsRef.current),
461
516
  },
462
517
  );
463
518
 
@@ -603,6 +603,15 @@ export interface ThemeConfig {
603
603
  * @default 'soft'
604
604
  */
605
605
  radius?: Radius;
606
+
607
+ /**
608
+ * Extra CSS injected into the Elements shadow root after the built-in
609
+ * stylesheet. Elements renders inside a shadow DOM, so host-page styles
610
+ * cannot reach its internals — this is the supported escape hatch for
611
+ * embedders that need to restyle specific components (targeting the
612
+ * stable `aui-*` class hooks).
613
+ */
614
+ customCss?: string;
606
615
  }
607
616
 
608
617
  export interface ComponentOverrides {