@melony/react 0.1.18 → 0.1.22
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/index.cjs +131 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +133 -108
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.cts
CHANGED
|
@@ -2,6 +2,7 @@ import * as React$1 from 'react';
|
|
|
2
2
|
import React__default, { ReactNode } from 'react';
|
|
3
3
|
import { ClientState, MelonyClient } from 'melony/client';
|
|
4
4
|
import { Role, Event, UINode } from 'melony';
|
|
5
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
5
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
7
|
|
|
7
8
|
interface User {
|
|
@@ -63,12 +64,18 @@ interface MelonyContextValue extends ClientState {
|
|
|
63
64
|
}) => Promise<void>;
|
|
64
65
|
reset: (events?: Event[]) => void;
|
|
65
66
|
client: MelonyClient;
|
|
67
|
+
config?: {
|
|
68
|
+
starterPrompts: any[];
|
|
69
|
+
options: any[];
|
|
70
|
+
};
|
|
66
71
|
}
|
|
67
72
|
declare const MelonyContext: React__default.Context<MelonyContextValue | undefined>;
|
|
68
73
|
interface MelonyClientProviderProps {
|
|
69
74
|
children: ReactNode;
|
|
70
75
|
client: MelonyClient;
|
|
71
76
|
initialEvents?: Event[];
|
|
77
|
+
queryClient?: QueryClient;
|
|
78
|
+
configApi?: string;
|
|
72
79
|
}
|
|
73
80
|
declare const MelonyClientProvider: React__default.FC<MelonyClientProviderProps>;
|
|
74
81
|
|
|
@@ -136,7 +143,7 @@ interface ThreadProps {
|
|
|
136
143
|
autoFocus?: boolean;
|
|
137
144
|
defaultSelectedIds?: string[];
|
|
138
145
|
}
|
|
139
|
-
declare function Thread({ className, placeholder, starterPrompts, onStarterPromptClick, options, autoFocus, defaultSelectedIds, }: ThreadProps): react_jsx_runtime.JSX.Element;
|
|
146
|
+
declare function Thread({ className, placeholder, starterPrompts: localStarterPrompts, onStarterPromptClick, options: localOptions, autoFocus, defaultSelectedIds, }: ThreadProps): react_jsx_runtime.JSX.Element;
|
|
140
147
|
|
|
141
148
|
interface ComposerProps {
|
|
142
149
|
value: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as React$1 from 'react';
|
|
|
2
2
|
import React__default, { ReactNode } from 'react';
|
|
3
3
|
import { ClientState, MelonyClient } from 'melony/client';
|
|
4
4
|
import { Role, Event, UINode } from 'melony';
|
|
5
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
5
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
7
|
|
|
7
8
|
interface User {
|
|
@@ -63,12 +64,18 @@ interface MelonyContextValue extends ClientState {
|
|
|
63
64
|
}) => Promise<void>;
|
|
64
65
|
reset: (events?: Event[]) => void;
|
|
65
66
|
client: MelonyClient;
|
|
67
|
+
config?: {
|
|
68
|
+
starterPrompts: any[];
|
|
69
|
+
options: any[];
|
|
70
|
+
};
|
|
66
71
|
}
|
|
67
72
|
declare const MelonyContext: React__default.Context<MelonyContextValue | undefined>;
|
|
68
73
|
interface MelonyClientProviderProps {
|
|
69
74
|
children: ReactNode;
|
|
70
75
|
client: MelonyClient;
|
|
71
76
|
initialEvents?: Event[];
|
|
77
|
+
queryClient?: QueryClient;
|
|
78
|
+
configApi?: string;
|
|
72
79
|
}
|
|
73
80
|
declare const MelonyClientProvider: React__default.FC<MelonyClientProviderProps>;
|
|
74
81
|
|
|
@@ -136,7 +143,7 @@ interface ThreadProps {
|
|
|
136
143
|
autoFocus?: boolean;
|
|
137
144
|
defaultSelectedIds?: string[];
|
|
138
145
|
}
|
|
139
|
-
declare function Thread({ className, placeholder, starterPrompts, onStarterPromptClick, options, autoFocus, defaultSelectedIds, }: ThreadProps): react_jsx_runtime.JSX.Element;
|
|
146
|
+
declare function Thread({ className, placeholder, starterPrompts: localStarterPrompts, onStarterPromptClick, options: localOptions, autoFocus, defaultSelectedIds, }: ThreadProps): react_jsx_runtime.JSX.Element;
|
|
140
147
|
|
|
141
148
|
interface ComposerProps {
|
|
142
149
|
value: string;
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import * as React10 from 'react';
|
|
2
|
-
import React10__default, { createContext, useState,
|
|
2
|
+
import React10__default, { createContext, useState, useCallback, useEffect, useMemo, useContext, useRef } from 'react';
|
|
3
|
+
import { NuqsAdapter } from 'nuqs/adapters/react';
|
|
4
|
+
import { QueryClient, QueryClientProvider, useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
|
|
3
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
6
|
import { generateId } from 'melony/client';
|
|
7
|
+
import { useQueryState, parseAsString } from 'nuqs';
|
|
5
8
|
import { clsx } from 'clsx';
|
|
6
9
|
import { twMerge } from 'tailwind-merge';
|
|
7
10
|
import { Button as Button$1 } from '@base-ui/react/button';
|
|
8
11
|
import { cva } from 'class-variance-authority';
|
|
9
12
|
import * as ICONS from '@tabler/icons-react';
|
|
10
|
-
import { IconChevronDown, IconLoader2, IconArrowUp, IconPlus, IconMessage, IconTrash, IconHistory, IconX, IconArrowLeft, IconChevronLeft, IconChevronRight, IconUser, IconLogout, IconBrandGoogle, IconDeviceDesktop, IconMoon, IconSun, IconCheck,
|
|
13
|
+
import { IconChevronDown, IconLoader2, IconArrowUp, IconPlus, IconMessage, IconTrash, IconHistory, IconX, IconArrowLeft, IconChevronLeft, IconChevronRight, IconUser, IconLogout, IconBrandGoogle, IconDeviceDesktop, IconMoon, IconSun, IconCheck, IconChevronUp, IconSelector } from '@tabler/icons-react';
|
|
11
14
|
import { Menu } from '@base-ui/react/menu';
|
|
12
15
|
import { Separator as Separator$1 } from '@base-ui/react/separator';
|
|
13
16
|
import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
|
|
@@ -48,12 +51,28 @@ function groupEventsToMessages(events) {
|
|
|
48
51
|
var MelonyContext = createContext(
|
|
49
52
|
void 0
|
|
50
53
|
);
|
|
51
|
-
var
|
|
54
|
+
var defaultQueryClient = new QueryClient({
|
|
55
|
+
defaultOptions: {
|
|
56
|
+
queries: {
|
|
57
|
+
retry: false,
|
|
58
|
+
refetchOnWindowFocus: false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
var MelonyContextProviderInner = ({
|
|
52
63
|
children,
|
|
53
64
|
client,
|
|
54
|
-
initialEvents
|
|
65
|
+
initialEvents,
|
|
66
|
+
configApi,
|
|
67
|
+
setContextValue
|
|
55
68
|
}) => {
|
|
56
69
|
const [state, setState] = useState(client.getState());
|
|
70
|
+
const { data: config } = useQuery({
|
|
71
|
+
queryKey: ["melony-config", configApi],
|
|
72
|
+
queryFn: () => client.getConfig(configApi),
|
|
73
|
+
enabled: !!configApi,
|
|
74
|
+
staleTime: Infinity
|
|
75
|
+
});
|
|
57
76
|
useEffect(() => {
|
|
58
77
|
if (initialEvents && initialEvents.length > 0 && client.getState().events.length === 0) {
|
|
59
78
|
client.reset(initialEvents);
|
|
@@ -84,11 +103,34 @@ var MelonyClientProvider = ({
|
|
|
84
103
|
messages: groupEventsToMessages(state.events),
|
|
85
104
|
sendEvent,
|
|
86
105
|
reset,
|
|
87
|
-
client
|
|
106
|
+
client,
|
|
107
|
+
config
|
|
88
108
|
}),
|
|
89
|
-
[state, sendEvent, reset, client]
|
|
109
|
+
[state, sendEvent, reset, client, config]
|
|
90
110
|
);
|
|
91
|
-
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
setContextValue(value);
|
|
113
|
+
}, [value, setContextValue]);
|
|
114
|
+
return /* @__PURE__ */ jsx(NuqsAdapter, { children });
|
|
115
|
+
};
|
|
116
|
+
var MelonyClientProvider = ({
|
|
117
|
+
children,
|
|
118
|
+
client,
|
|
119
|
+
initialEvents,
|
|
120
|
+
queryClient = defaultQueryClient,
|
|
121
|
+
configApi
|
|
122
|
+
}) => {
|
|
123
|
+
const [contextValue, setContextValue] = useState(void 0);
|
|
124
|
+
return /* @__PURE__ */ jsx(MelonyContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(
|
|
125
|
+
MelonyContextProviderInner,
|
|
126
|
+
{
|
|
127
|
+
client,
|
|
128
|
+
initialEvents,
|
|
129
|
+
configApi,
|
|
130
|
+
setContextValue,
|
|
131
|
+
children
|
|
132
|
+
}
|
|
133
|
+
) }) });
|
|
92
134
|
};
|
|
93
135
|
var AuthContext = createContext(
|
|
94
136
|
void 0
|
|
@@ -161,111 +203,75 @@ var ThreadProvider = ({
|
|
|
161
203
|
service,
|
|
162
204
|
initialThreadId: providedInitialThreadId
|
|
163
205
|
}) => {
|
|
206
|
+
const queryClient = useQueryClient();
|
|
164
207
|
const defaultInitialThreadId = useMemo(() => generateId(), []);
|
|
165
208
|
const initialThreadId = providedInitialThreadId || defaultInitialThreadId;
|
|
166
|
-
const [
|
|
167
|
-
|
|
168
|
-
initialThreadId
|
|
209
|
+
const [activeThreadId, setActiveThreadId] = useQueryState(
|
|
210
|
+
"threadId",
|
|
211
|
+
parseAsString.withDefault(initialThreadId)
|
|
169
212
|
);
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
213
|
+
const {
|
|
214
|
+
data: threads = [],
|
|
215
|
+
isLoading,
|
|
216
|
+
error: threadsError,
|
|
217
|
+
refetch: refreshThreads
|
|
218
|
+
} = useQuery({
|
|
219
|
+
queryKey: ["threads"],
|
|
220
|
+
queryFn: () => service.getThreads()
|
|
221
|
+
});
|
|
222
|
+
const { data: threadEvents = [], isLoading: isLoadingEvents } = useQuery({
|
|
223
|
+
queryKey: ["threads", activeThreadId, "events"],
|
|
224
|
+
queryFn: () => service.getEvents(activeThreadId),
|
|
225
|
+
enabled: !!activeThreadId
|
|
226
|
+
});
|
|
227
|
+
const createMutation = useMutation({
|
|
228
|
+
mutationFn: async () => {
|
|
229
|
+
const newId = service.createThread ? await service.createThread() : generateId();
|
|
230
|
+
return newId;
|
|
231
|
+
},
|
|
232
|
+
onSuccess: async (newId) => {
|
|
233
|
+
await queryClient.invalidateQueries({ queryKey: ["threads"] });
|
|
234
|
+
await setActiveThreadId(newId);
|
|
186
235
|
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
236
|
+
});
|
|
237
|
+
const deleteMutation = useMutation({
|
|
238
|
+
mutationFn: (threadId) => service.deleteThread(threadId),
|
|
239
|
+
onSuccess: async (_, threadId) => {
|
|
240
|
+
await queryClient.invalidateQueries({ queryKey: ["threads"] });
|
|
241
|
+
if (activeThreadId === threadId) {
|
|
242
|
+
const remainingThreads = threads.filter((t) => t.id !== threadId);
|
|
243
|
+
const nextId = remainingThreads.length > 0 ? remainingThreads[0].id : null;
|
|
244
|
+
await setActiveThreadId(nextId);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
const selectThread = useCallback(
|
|
249
|
+
(threadId) => {
|
|
250
|
+
setActiveThreadId(threadId);
|
|
251
|
+
},
|
|
252
|
+
[setActiveThreadId]
|
|
253
|
+
);
|
|
194
254
|
const createThread = useCallback(async () => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
id: newId,
|
|
198
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
199
|
-
};
|
|
200
|
-
setThreads((prev) => [newThread, ...prev]);
|
|
201
|
-
setActiveThreadId(newId);
|
|
202
|
-
return newId;
|
|
203
|
-
}, [service]);
|
|
255
|
+
return createMutation.mutateAsync();
|
|
256
|
+
}, [createMutation]);
|
|
204
257
|
const deleteThread = useCallback(
|
|
205
258
|
async (threadId) => {
|
|
206
|
-
|
|
207
|
-
await service.deleteThread(threadId);
|
|
208
|
-
setThreads((prev) => {
|
|
209
|
-
const remainingThreads = prev.filter((t) => t.id !== threadId);
|
|
210
|
-
setActiveThreadId((current) => {
|
|
211
|
-
if (current === threadId) {
|
|
212
|
-
return remainingThreads.length > 0 ? remainingThreads[0].id : null;
|
|
213
|
-
}
|
|
214
|
-
return current;
|
|
215
|
-
});
|
|
216
|
-
return remainingThreads;
|
|
217
|
-
});
|
|
218
|
-
} catch (err) {
|
|
219
|
-
const error2 = err instanceof Error ? err : new Error("Failed to delete thread");
|
|
220
|
-
setError(error2);
|
|
221
|
-
throw error2;
|
|
222
|
-
}
|
|
259
|
+
return deleteMutation.mutateAsync(threadId);
|
|
223
260
|
},
|
|
224
|
-
[
|
|
261
|
+
[deleteMutation]
|
|
225
262
|
);
|
|
226
|
-
const refreshThreads = useCallback(async () => {
|
|
227
|
-
await fetchThreads();
|
|
228
|
-
}, [fetchThreads]);
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
if (!activeThreadId) {
|
|
231
|
-
setThreadEvents([]);
|
|
232
|
-
setIsLoadingEvents(false);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
let cancelled = false;
|
|
236
|
-
const fetchEvents = async () => {
|
|
237
|
-
setIsLoadingEvents(true);
|
|
238
|
-
try {
|
|
239
|
-
const events = await service.getEvents(activeThreadId);
|
|
240
|
-
if (!cancelled) {
|
|
241
|
-
setThreadEvents(events);
|
|
242
|
-
}
|
|
243
|
-
} catch (err) {
|
|
244
|
-
if (!cancelled) {
|
|
245
|
-
console.error("Failed to fetch events:", err);
|
|
246
|
-
setThreadEvents([]);
|
|
247
|
-
}
|
|
248
|
-
} finally {
|
|
249
|
-
if (!cancelled) {
|
|
250
|
-
setIsLoadingEvents(false);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
fetchEvents();
|
|
255
|
-
return () => {
|
|
256
|
-
cancelled = true;
|
|
257
|
-
};
|
|
258
|
-
}, [activeThreadId, service]);
|
|
259
263
|
const value = useMemo(
|
|
260
264
|
() => ({
|
|
261
265
|
threads,
|
|
262
266
|
activeThreadId,
|
|
263
267
|
isLoading,
|
|
264
|
-
error,
|
|
268
|
+
error: threadsError || null,
|
|
265
269
|
selectThread,
|
|
266
270
|
createThread,
|
|
267
271
|
deleteThread,
|
|
268
|
-
refreshThreads
|
|
272
|
+
refreshThreads: async () => {
|
|
273
|
+
await refreshThreads();
|
|
274
|
+
},
|
|
269
275
|
threadEvents,
|
|
270
276
|
isLoadingEvents
|
|
271
277
|
}),
|
|
@@ -273,7 +279,7 @@ var ThreadProvider = ({
|
|
|
273
279
|
threads,
|
|
274
280
|
activeThreadId,
|
|
275
281
|
isLoading,
|
|
276
|
-
|
|
282
|
+
threadsError,
|
|
277
283
|
selectThread,
|
|
278
284
|
createThread,
|
|
279
285
|
deleteThread,
|
|
@@ -343,9 +349,16 @@ var useMelony = (options) => {
|
|
|
343
349
|
}
|
|
344
350
|
const { client, reset } = context;
|
|
345
351
|
const { initialEvents } = options || {};
|
|
352
|
+
const prevInitialEventsRef = useRef(void 0);
|
|
346
353
|
useEffect(() => {
|
|
347
|
-
|
|
348
|
-
|
|
354
|
+
const currentSerialized = initialEvents ? JSON.stringify(initialEvents) : void 0;
|
|
355
|
+
if (currentSerialized !== prevInitialEventsRef.current) {
|
|
356
|
+
if (initialEvents) {
|
|
357
|
+
reset(initialEvents);
|
|
358
|
+
} else {
|
|
359
|
+
reset([]);
|
|
360
|
+
}
|
|
361
|
+
prevInitialEventsRef.current = currentSerialized;
|
|
349
362
|
}
|
|
350
363
|
}, [client, initialEvents, reset]);
|
|
351
364
|
return context;
|
|
@@ -2316,13 +2329,18 @@ function MessageList({
|
|
|
2316
2329
|
function Thread({
|
|
2317
2330
|
className,
|
|
2318
2331
|
placeholder = "Type a message...",
|
|
2319
|
-
starterPrompts,
|
|
2332
|
+
starterPrompts: localStarterPrompts,
|
|
2320
2333
|
onStarterPromptClick,
|
|
2321
|
-
options,
|
|
2334
|
+
options: localOptions,
|
|
2322
2335
|
autoFocus = false,
|
|
2323
2336
|
defaultSelectedIds
|
|
2324
2337
|
}) {
|
|
2325
|
-
const {
|
|
2338
|
+
const { activeThreadId, threadEvents, isLoadingEvents } = useThreads();
|
|
2339
|
+
const { messages, isLoading, error, sendEvent, loadingStatus, config } = useMelony({
|
|
2340
|
+
initialEvents: threadEvents
|
|
2341
|
+
});
|
|
2342
|
+
const starterPrompts = localStarterPrompts ?? config?.starterPrompts;
|
|
2343
|
+
const options = localOptions ?? config?.options;
|
|
2326
2344
|
const [input, setInput] = useState("");
|
|
2327
2345
|
const messagesEndRef = useRef(null);
|
|
2328
2346
|
useEffect(() => {
|
|
@@ -2338,7 +2356,7 @@ function Thread({
|
|
|
2338
2356
|
role: "user",
|
|
2339
2357
|
data: { content: text }
|
|
2340
2358
|
},
|
|
2341
|
-
{ state }
|
|
2359
|
+
{ state: { ...state, threadId: activeThreadId ?? void 0 } }
|
|
2342
2360
|
);
|
|
2343
2361
|
};
|
|
2344
2362
|
const handleStarterPromptClick = (prompt) => {
|
|
@@ -2348,21 +2366,21 @@ function Thread({
|
|
|
2348
2366
|
handleSubmit(void 0, prompt);
|
|
2349
2367
|
}
|
|
2350
2368
|
};
|
|
2351
|
-
const showStarterPrompts = messages.length === 0 && starterPrompts && starterPrompts.length > 0;
|
|
2369
|
+
const showStarterPrompts = messages.length === 0 && starterPrompts && starterPrompts.length > 0 && !isLoadingEvents;
|
|
2352
2370
|
return /* @__PURE__ */ jsxs(
|
|
2353
2371
|
"div",
|
|
2354
2372
|
{
|
|
2355
2373
|
className: cn("relative flex flex-col h-full bg-background", className),
|
|
2356
2374
|
children: [
|
|
2357
2375
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 pb-36", children: [
|
|
2358
|
-
/* @__PURE__ */
|
|
2376
|
+
/* @__PURE__ */ jsx(
|
|
2359
2377
|
"div",
|
|
2360
2378
|
{
|
|
2361
2379
|
className: cn(
|
|
2362
2380
|
"max-w-4xl mx-auto w-full p-4",
|
|
2363
2381
|
showStarterPrompts && "min-h-full flex flex-col"
|
|
2364
2382
|
),
|
|
2365
|
-
children: [
|
|
2383
|
+
children: isLoadingEvents && messages.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-20", children: /* @__PURE__ */ jsx(LoadingIndicator, { status: { message: "Loading messages..." } }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2366
2384
|
showStarterPrompts && /* @__PURE__ */ jsx(
|
|
2367
2385
|
StarterPrompts,
|
|
2368
2386
|
{
|
|
@@ -2379,7 +2397,7 @@ function Thread({
|
|
|
2379
2397
|
loadingStatus
|
|
2380
2398
|
}
|
|
2381
2399
|
)
|
|
2382
|
-
]
|
|
2400
|
+
] })
|
|
2383
2401
|
}
|
|
2384
2402
|
),
|
|
2385
2403
|
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
@@ -2422,7 +2440,14 @@ var ThreadList = ({
|
|
|
2422
2440
|
emptyState,
|
|
2423
2441
|
onThreadSelect
|
|
2424
2442
|
}) => {
|
|
2425
|
-
const {
|
|
2443
|
+
const {
|
|
2444
|
+
threads,
|
|
2445
|
+
activeThreadId,
|
|
2446
|
+
selectThread,
|
|
2447
|
+
createThread,
|
|
2448
|
+
deleteThread,
|
|
2449
|
+
isLoading
|
|
2450
|
+
} = useThreads();
|
|
2426
2451
|
const handleThreadClick = (threadId) => {
|
|
2427
2452
|
if (threadId !== activeThreadId) {
|
|
2428
2453
|
selectThread(threadId);
|
|
@@ -2473,7 +2498,7 @@ var ThreadList = ({
|
|
|
2473
2498
|
]
|
|
2474
2499
|
}
|
|
2475
2500
|
) }),
|
|
2476
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-muted-foreground", children: emptyState || /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2501
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: isLoading && threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx(IconLoader2, { className: "size-5 animate-spin text-muted-foreground" }) }) : threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-muted-foreground", children: emptyState || /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2477
2502
|
/* @__PURE__ */ jsx(IconMessage, { className: "size-8 mx-auto opacity-50" }),
|
|
2478
2503
|
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "No threads yet" }),
|
|
2479
2504
|
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: handleNewThread, children: "Start a conversation" })
|