@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.
- package/dist/components/assistant-ui/markdown-text.d.ts +1 -1
- package/dist/contexts/ThreadMetaContext.d.ts +20 -0
- package/dist/elements.cjs +1 -1
- package/dist/elements.css +1 -1
- package/dist/elements.js +1 -1
- package/dist/hooks/useGramThreadListAdapter.d.ts +8 -0
- package/dist/{index-BziIHO9O.js → index-BdBraSNn.js} +8698 -8583
- package/dist/index-BdBraSNn.js.map +1 -0
- package/dist/index-Bl5cH0sz.cjs +194 -0
- package/dist/index-Bl5cH0sz.cjs.map +1 -0
- package/dist/index-Bv-yE4G1.js.map +1 -1
- package/dist/index-CGBkMd0d.cjs.map +1 -1
- package/dist/{profiler-xLXOPfmj.js → profiler-Cl-9cG3B.js} +2 -2
- package/dist/{profiler-xLXOPfmj.js.map → profiler-Cl-9cG3B.js.map} +1 -1
- package/dist/{profiler-DuJEf_S6.cjs → profiler-ttCkbP-N.cjs} +2 -2
- package/dist/{profiler-DuJEf_S6.cjs.map → profiler-ttCkbP-N.cjs.map} +1 -1
- package/dist/{startRecording-qKnXr4lw.js → startRecording-C41DbnxY.js} +2 -2
- package/dist/{startRecording-qKnXr4lw.js.map → startRecording-C41DbnxY.js.map} +1 -1
- package/dist/{startRecording-C2XF9-Ol.cjs → startRecording-DLCeKyz9.cjs} +2 -2
- package/dist/{startRecording-C2XF9-Ol.cjs.map → startRecording-DLCeKyz9.cjs.map} +1 -1
- package/dist/types/index.d.ts +8 -0
- package/package.json +3 -3
- package/src/components/ShadowRoot.tsx +26 -0
- package/src/components/assistant-ui/thinking-indicator.tsx +42 -0
- package/src/components/assistant-ui/thread-list.tsx +50 -5
- package/src/contexts/ElementsProvider.tsx +31 -5
- package/src/contexts/ThreadMetaContext.ts +27 -0
- package/src/hooks/useGramThreadListAdapter.tsx +67 -12
- package/src/types/index.ts +9 -0
- package/dist/index-7fTI_vaV.cjs +0 -194
- package/dist/index-7fTI_vaV.cjs.map +0 -1
- 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
|
|
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
|
-
|
|
154
|
+
getHeaders: () => Promise<Record<string, string>>,
|
|
120
155
|
store: AssistantApi,
|
|
121
156
|
getTransformChatMessage?: () => ChatMessageTransform | undefined,
|
|
122
157
|
) {
|
|
123
158
|
this.apiUrl = apiUrl;
|
|
124
|
-
this.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
<
|
|
285
|
-
{
|
|
286
|
-
|
|
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,
|
|
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
|
|
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
|
|
515
|
+
headers: await resolveAdapterHeaders(optionsRef.current),
|
|
461
516
|
},
|
|
462
517
|
);
|
|
463
518
|
|
package/src/types/index.ts
CHANGED
|
@@ -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 {
|