@melony/react 0.1.11 → 0.1.14

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.js CHANGED
@@ -1,25 +1,24 @@
1
- import * as React9 from 'react';
1
+ import * as React10 from 'react';
2
2
  import { createContext, useState, useEffect, useCallback, useMemo, useContext, useRef } from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { generateId } from 'melony/client';
3
5
  import { clsx } from 'clsx';
4
6
  import { twMerge } from 'tailwind-merge';
5
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
- import { generateId } from '@melony/core/client';
7
- import { Separator as Separator$1 } from '@base-ui/react/separator';
7
+ import { Button as Button$1 } from '@base-ui/react/button';
8
+ import { cva } from 'class-variance-authority';
8
9
  import * as ICONS from '@tabler/icons-react';
9
- import { IconArrowUp, IconPlus, IconMessage, IconTrash, IconArrowLeft, IconHistory, IconX, IconUser, IconLogout, IconBrandGoogle, IconSelector, IconCheck, IconChevronUp, IconChevronDown } from '@tabler/icons-react';
10
+ import { IconArrowUp, IconPlus, IconMessage, IconTrash, IconHistory, IconX, IconArrowLeft, IconChevronLeft, IconChevronRight, IconUser, IconLogout, IconBrandGoogle, IconDeviceDesktop, IconMoon, IconSun, IconSelector, IconCheck, IconChevronUp, IconChevronDown } from '@tabler/icons-react';
11
+ import { Separator as Separator$1 } from '@base-ui/react/separator';
10
12
  import { mergeProps } from '@base-ui/react/merge-props';
11
13
  import { useRender } from '@base-ui/react/use-render';
12
- import { cva } from 'class-variance-authority';
13
14
  import { Input as Input$1 } from '@base-ui/react/input';
14
15
  import { Select as Select$1 } from '@base-ui/react/select';
15
- import { Button as Button$1 } from '@base-ui/react/button';
16
16
  import { AlertDialog as AlertDialog$1 } from '@base-ui/react/alert-dialog';
17
17
  import { Menu } from '@base-ui/react/menu';
18
18
 
19
19
  // src/providers/melony-provider.tsx
20
- function cn(...inputs) {
21
- return twMerge(clsx(inputs));
22
- }
20
+
21
+ // src/lib/group-events-to-messages.ts
23
22
  function groupEventsToMessages(events) {
24
23
  if (events.length === 0) return [];
25
24
  const messages = [];
@@ -46,15 +45,22 @@ function groupEventsToMessages(events) {
46
45
  var MelonyContext = createContext(
47
46
  void 0
48
47
  );
49
- var MelonyProvider = ({
48
+ var MelonyClientProvider = ({
50
49
  children,
51
- client
50
+ client,
51
+ initialEvents
52
52
  }) => {
53
53
  const [state, setState] = useState(client.getState());
54
+ useEffect(() => {
55
+ if (initialEvents && initialEvents.length > 0 && client.getState().events.length === 0) {
56
+ client.reset(initialEvents);
57
+ }
58
+ }, [client, initialEvents]);
54
59
  useEffect(() => {
55
60
  setState(client.getState());
61
+ const unsubscribe = client.subscribe(setState);
56
62
  return () => {
57
- client.subscribe(setState);
63
+ unsubscribe();
58
64
  };
59
65
  }, [client]);
60
66
  const sendEvent = useCallback(
@@ -65,16 +71,19 @@ var MelonyProvider = ({
65
71
  },
66
72
  [client]
67
73
  );
68
- const clear = useCallback(() => client.clear(), [client]);
74
+ const reset = useCallback(
75
+ (events) => client.reset(events),
76
+ [client]
77
+ );
69
78
  const value = useMemo(
70
79
  () => ({
71
80
  ...state,
72
81
  messages: groupEventsToMessages(state.events),
73
82
  sendEvent,
74
- clear,
83
+ reset,
75
84
  client
76
85
  }),
77
- [state, sendEvent, clear, client]
86
+ [state, sendEvent, reset, client]
78
87
  );
79
88
  return /* @__PURE__ */ jsx(MelonyContext.Provider, { value, children });
80
89
  };
@@ -141,7 +150,9 @@ var AuthProvider = ({
141
150
  }
142
151
  return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
143
152
  };
144
- var ThreadContext = createContext(void 0);
153
+ var ThreadContext = createContext(
154
+ void 0
155
+ );
145
156
  var ThreadProvider = ({
146
157
  children,
147
158
  service,
@@ -212,9 +223,6 @@ var ThreadProvider = ({
212
223
  const refreshThreads = useCallback(async () => {
213
224
  await fetchThreads();
214
225
  }, [fetchThreads]);
215
- const threadMessages = useMemo(() => {
216
- return groupEventsToMessages(threadEvents);
217
- }, [threadEvents]);
218
226
  useEffect(() => {
219
227
  if (!activeThreadId) {
220
228
  setThreadEvents([]);
@@ -256,7 +264,6 @@ var ThreadProvider = ({
256
264
  deleteThread,
257
265
  refreshThreads,
258
266
  threadEvents,
259
- threadMessages,
260
267
  isLoadingEvents
261
268
  }),
262
269
  [
@@ -269,17 +276,75 @@ var ThreadProvider = ({
269
276
  deleteThread,
270
277
  refreshThreads,
271
278
  threadEvents,
272
- threadMessages,
273
279
  isLoadingEvents
274
280
  ]
275
281
  );
276
282
  return /* @__PURE__ */ jsx(ThreadContext.Provider, { value, children });
277
283
  };
278
- var useMelony = () => {
284
+ var ThemeContext = createContext(void 0);
285
+ function ThemeProvider({ children }) {
286
+ const [theme, setThemeState] = useState("system");
287
+ const [resolvedTheme, setResolvedTheme] = useState("light");
288
+ useEffect(() => {
289
+ if (typeof window !== "undefined") {
290
+ const stored = localStorage.getItem("theme");
291
+ if (stored) {
292
+ setThemeState(stored);
293
+ }
294
+ }
295
+ }, []);
296
+ useEffect(() => {
297
+ if (typeof window !== "undefined") {
298
+ if (theme === "system") {
299
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
300
+ const updateResolvedTheme = () => {
301
+ setResolvedTheme(mediaQuery.matches ? "dark" : "light");
302
+ };
303
+ updateResolvedTheme();
304
+ mediaQuery.addEventListener("change", updateResolvedTheme);
305
+ return () => mediaQuery.removeEventListener("change", updateResolvedTheme);
306
+ } else {
307
+ setResolvedTheme(theme);
308
+ }
309
+ }
310
+ }, [theme]);
311
+ useEffect(() => {
312
+ if (typeof window !== "undefined") {
313
+ const root = document.documentElement;
314
+ if (resolvedTheme === "dark") {
315
+ root.classList.add("dark");
316
+ } else {
317
+ root.classList.remove("dark");
318
+ }
319
+ }
320
+ }, [resolvedTheme]);
321
+ const setTheme = (newTheme) => {
322
+ setThemeState(newTheme);
323
+ if (typeof window !== "undefined") {
324
+ localStorage.setItem("theme", newTheme);
325
+ }
326
+ };
327
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, setTheme, resolvedTheme }, children });
328
+ }
329
+ function useTheme() {
330
+ const context = useContext(ThemeContext);
331
+ if (context === void 0) {
332
+ throw new Error("useTheme must be used within a ThemeProvider");
333
+ }
334
+ return context;
335
+ }
336
+ var useMelony = (options) => {
279
337
  const context = useContext(MelonyContext);
280
338
  if (context === void 0) {
281
- throw new Error("useMelony must be used within a MelonyProvider");
339
+ throw new Error("useMelony must be used within a MelonyClientProvider");
282
340
  }
341
+ const { client, reset } = context;
342
+ const { initialEvents } = options || {};
343
+ useEffect(() => {
344
+ if (initialEvents && initialEvents.length > 0 && client.getState().events.length === 0) {
345
+ reset(initialEvents);
346
+ }
347
+ }, [client, initialEvents, reset]);
283
348
  return context;
284
349
  };
285
350
  var useAuth = () => {
@@ -296,6 +361,103 @@ var useThreads = () => {
296
361
  }
297
362
  return context;
298
363
  };
364
+ function cn(...inputs) {
365
+ return twMerge(clsx(inputs));
366
+ }
367
+ var buttonVariants = cva(
368
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
369
+ {
370
+ variants: {
371
+ variant: {
372
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
373
+ outline: "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
374
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
375
+ ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
376
+ destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
377
+ link: "text-primary underline-offset-4 hover:underline"
378
+ },
379
+ size: {
380
+ default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
381
+ xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
382
+ sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
383
+ lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
384
+ icon: "size-9",
385
+ "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
386
+ "icon-sm": "size-8",
387
+ "icon-lg": "size-10"
388
+ }
389
+ },
390
+ defaultVariants: {
391
+ variant: "default",
392
+ size: "default"
393
+ }
394
+ }
395
+ );
396
+ function Button({
397
+ className,
398
+ variant = "default",
399
+ size = "default",
400
+ ...props
401
+ }) {
402
+ return /* @__PURE__ */ jsx(
403
+ Button$1,
404
+ {
405
+ "data-slot": "button",
406
+ className: cn(buttonVariants({ variant, size, className })),
407
+ ...props
408
+ }
409
+ );
410
+ }
411
+ function Textarea({ className, ...props }) {
412
+ return /* @__PURE__ */ jsx(
413
+ "textarea",
414
+ {
415
+ "data-slot": "textarea",
416
+ className: cn(
417
+ "border-input bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 resize-none rounded-xl border px-3 py-3 text-base transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
418
+ className
419
+ ),
420
+ ...props
421
+ }
422
+ );
423
+ }
424
+ function Composer({
425
+ value,
426
+ onChange,
427
+ onSubmit,
428
+ placeholder = "Type a message...",
429
+ isLoading,
430
+ className
431
+ }) {
432
+ const handleKeyDown = (e) => {
433
+ if (e.key === "Enter" && !e.shiftKey) {
434
+ e.preventDefault();
435
+ onSubmit();
436
+ }
437
+ };
438
+ return /* @__PURE__ */ jsx("div", { className: cn("relative flex flex-col w-full", className), children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col w-full border-input border-[1.5px] rounded-3xl bg-background shadow-sm focus-within:border-ring transition-all p-2", children: [
439
+ /* @__PURE__ */ jsx(
440
+ Textarea,
441
+ {
442
+ value,
443
+ onChange: (e) => onChange(e.target.value),
444
+ onKeyDown: handleKeyDown,
445
+ placeholder,
446
+ className: "min-h-[44px] max-h-[200px] border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-3 py-2 text-[15px] resize-none"
447
+ }
448
+ ),
449
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end items-center", children: /* @__PURE__ */ jsx(
450
+ Button,
451
+ {
452
+ type: "submit",
453
+ disabled: !value.trim() && !isLoading || isLoading,
454
+ size: "icon-lg",
455
+ onClick: () => onSubmit(),
456
+ children: /* @__PURE__ */ jsx(IconArrowUp, { className: "h-5 w-5" })
457
+ }
458
+ ) })
459
+ ] }) });
460
+ }
299
461
  function Card({
300
462
  className,
301
463
  size = "default",
@@ -364,7 +526,7 @@ var Card2 = ({
364
526
  return /* @__PURE__ */ jsxs(
365
527
  Card,
366
528
  {
367
- className: cn("w-full max-w-2xl shadow-sm", className),
529
+ className: cn("min-w-96", className),
368
530
  style,
369
531
  children: [
370
532
  (title || subtitle) && /* @__PURE__ */ jsxs(CardHeader, { className: "pb-3", children: [
@@ -1185,19 +1347,6 @@ var Input2 = ({
1185
1347
  )
1186
1348
  ] });
1187
1349
  };
1188
- function Textarea({ className, ...props }) {
1189
- return /* @__PURE__ */ jsx(
1190
- "textarea",
1191
- {
1192
- "data-slot": "textarea",
1193
- className: cn(
1194
- "border-input bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 resize-none rounded-xl border px-3 py-3 text-base transition-colors focus-visible:ring-[3px] aria-invalid:ring-[3px] md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50",
1195
- className
1196
- ),
1197
- ...props
1198
- }
1199
- );
1200
- }
1201
1350
  var Textarea2 = ({
1202
1351
  placeholder,
1203
1352
  defaultValue,
@@ -1569,50 +1718,6 @@ var RadioGroup = ({
1569
1718
  )
1570
1719
  ] });
1571
1720
  };
1572
- var buttonVariants = cva(
1573
- "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
1574
- {
1575
- variants: {
1576
- variant: {
1577
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
1578
- outline: "border-border bg-input/30 hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
1579
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
1580
- ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
1581
- destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
1582
- link: "text-primary underline-offset-4 hover:underline"
1583
- },
1584
- size: {
1585
- default: "h-9 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
1586
- xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
1587
- sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
1588
- lg: "h-10 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
1589
- icon: "size-9",
1590
- "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
1591
- "icon-sm": "size-8",
1592
- "icon-lg": "size-10"
1593
- }
1594
- },
1595
- defaultVariants: {
1596
- variant: "default",
1597
- size: "default"
1598
- }
1599
- }
1600
- );
1601
- function Button({
1602
- className,
1603
- variant = "default",
1604
- size = "default",
1605
- ...props
1606
- }) {
1607
- return /* @__PURE__ */ jsx(
1608
- Button$1,
1609
- {
1610
- "data-slot": "button",
1611
- className: cn(buttonVariants({ variant, size, className })),
1612
- ...props
1613
- }
1614
- );
1615
- }
1616
1721
  var Button2 = ({
1617
1722
  label,
1618
1723
  variant = "primary",
@@ -1629,6 +1734,8 @@ var Button2 = ({
1629
1734
  secondary: "secondary",
1630
1735
  danger: "destructive",
1631
1736
  outline: "outline",
1737
+ ghost: "ghost",
1738
+ link: "link",
1632
1739
  success: "default"
1633
1740
  // Success doesn't have a direct shadcn mapping in base variant, default is usually primary
1634
1741
  };
@@ -1697,6 +1804,32 @@ var Form = ({ children, onSubmitAction, className, style }) => {
1697
1804
  }
1698
1805
  );
1699
1806
  };
1807
+ function StarterPrompts({
1808
+ prompts,
1809
+ onPromptClick
1810
+ }) {
1811
+ if (!prompts || prompts.length === 0) {
1812
+ return null;
1813
+ }
1814
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-4 animate-in fade-in slide-in-from-bottom-4 duration-500 mt-auto max-w-2xl", children: [
1815
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold tracking-tight", children: "What can I help with today?" }) }),
1816
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 w-full", children: prompts.map((item, index) => /* @__PURE__ */ jsx(
1817
+ Button2,
1818
+ {
1819
+ label: item.label,
1820
+ variant: "ghost",
1821
+ size: "lg",
1822
+ onClickAction: {
1823
+ type: "text",
1824
+ role: "user",
1825
+ data: { content: item.prompt }
1826
+ },
1827
+ className: "w-full justify-start"
1828
+ },
1829
+ index
1830
+ )) })
1831
+ ] });
1832
+ }
1700
1833
  function UIRenderer({ node }) {
1701
1834
  const { type, props, children } = node;
1702
1835
  const typeMap = {
@@ -1735,46 +1868,57 @@ function UIRenderer({ node }) {
1735
1868
  const componentProps = { ...props };
1736
1869
  return /* @__PURE__ */ jsx(Component, { ...componentProps, children: renderedChildren });
1737
1870
  }
1738
- function Composer({
1739
- value,
1740
- onChange,
1741
- onSubmit,
1742
- placeholder = "Type a message...",
1743
- isLoading,
1744
- className
1745
- }) {
1746
- const handleKeyDown = (e) => {
1747
- if (e.key === "Enter" && !e.shiftKey) {
1748
- e.preventDefault();
1749
- onSubmit();
1871
+ function MessageContent({ events }) {
1872
+ return /* @__PURE__ */ jsx(Fragment, { children: events.map((event, index) => {
1873
+ if (event.type === "text-delta") {
1874
+ return /* @__PURE__ */ jsx("span", { children: event.data?.delta }, index);
1750
1875
  }
1751
- };
1752
- return /* @__PURE__ */ jsx("div", { className: cn("relative flex flex-col w-full", className), children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col w-full border-input border-[1.5px] rounded-3xl bg-background shadow-sm focus-within:border-ring transition-all p-2", children: [
1753
- /* @__PURE__ */ jsx(
1754
- Textarea,
1755
- {
1756
- value,
1757
- onChange: (e) => onChange(e.target.value),
1758
- onKeyDown: handleKeyDown,
1759
- placeholder,
1760
- className: "min-h-[44px] max-h-[200px] border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 px-3 py-2 text-[15px] resize-none"
1761
- }
1762
- ),
1763
- /* @__PURE__ */ jsx("div", { className: "flex justify-end items-center px-2 pb-0.5", children: /* @__PURE__ */ jsx(
1764
- Button,
1765
- {
1766
- type: "submit",
1767
- disabled: !value.trim() && !isLoading || isLoading,
1768
- size: "icon",
1769
- onClick: () => onSubmit(),
1770
- className: cn(
1771
- "h-8 w-8 rounded-full transition-all shrink-0",
1772
- value.trim() ? "bg-foreground text-background hover:bg-foreground/90" : "bg-muted-foreground/20 text-muted-foreground/40"
1773
- ),
1774
- children: /* @__PURE__ */ jsx(IconArrowUp, { className: "h-5 w-5" })
1775
- }
1776
- ) })
1777
- ] }) });
1876
+ if (event.type === "text") {
1877
+ return /* @__PURE__ */ jsx("p", { children: event.data?.content || event.data?.text }, index);
1878
+ }
1879
+ if (event.ui) {
1880
+ return /* @__PURE__ */ jsx(UIRenderer, { node: event.ui }, index);
1881
+ }
1882
+ return null;
1883
+ }) });
1884
+ }
1885
+ function MessageBubble({ message }) {
1886
+ const isUser = message.role === "user";
1887
+ return /* @__PURE__ */ jsx(
1888
+ "div",
1889
+ {
1890
+ className: cn(
1891
+ "flex flex-col",
1892
+ isUser ? "items-end" : "items-start"
1893
+ ),
1894
+ children: /* @__PURE__ */ jsx(
1895
+ "div",
1896
+ {
1897
+ className: cn(
1898
+ "flex flex-col items-start max-w-[85%] rounded-2xl px-4 py-2 space-y-4 whitespace-pre-wrap",
1899
+ isUser ? "bg-primary text-primary-foreground" : "px-0 py-0 text-foreground"
1900
+ ),
1901
+ children: /* @__PURE__ */ jsx(MessageContent, { events: message.content })
1902
+ }
1903
+ )
1904
+ }
1905
+ );
1906
+ }
1907
+ function LoadingIndicator() {
1908
+ return /* @__PURE__ */ jsx("div", { className: "text-muted-foreground animate-pulse", children: "Thinking..." });
1909
+ }
1910
+ function ErrorDisplay({ error }) {
1911
+ return /* @__PURE__ */ jsx("div", { className: "text-destructive p-2 border border-destructive rounded-md bg-destructive/10", children: error.message });
1912
+ }
1913
+ function MessageList({ messages, isLoading, error }) {
1914
+ if (messages.length === 0) {
1915
+ return null;
1916
+ }
1917
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
1918
+ messages.map((message, index) => /* @__PURE__ */ jsx(MessageBubble, { message }, index)),
1919
+ isLoading && /* @__PURE__ */ jsx(LoadingIndicator, {}),
1920
+ error && /* @__PURE__ */ jsx(ErrorDisplay, { error })
1921
+ ] });
1778
1922
  }
1779
1923
  function Thread({
1780
1924
  className,
@@ -1806,57 +1950,38 @@ function Thread({
1806
1950
  handleSubmit(void 0, prompt);
1807
1951
  }
1808
1952
  };
1809
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full bg-background", className), children: [
1810
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-6", children: [
1811
- /* @__PURE__ */ jsxs("div", { className: "max-w-4xl mx-auto w-full", children: [
1812
- messages.length === 0 && starterPrompts && starterPrompts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center min-h-[300px] space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500", children: [
1813
- /* @__PURE__ */ jsx("div", { className: "text-center space-y-2", children: /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold tracking-tight", children: "What can I help with today?" }) }),
1814
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 w-full max-w-2xl px-4", children: starterPrompts.map((item, i) => /* @__PURE__ */ jsxs(
1815
- "button",
1816
- {
1817
- onClick: () => handleStarterPromptClick(item.prompt),
1818
- className: "flex items-center gap-3 p-4 rounded-xl border bg-card hover:bg-muted/50 transition-all text-left group",
1819
- children: [
1820
- item.icon && /* @__PURE__ */ jsx("div", { className: "p-2 rounded-lg bg-muted group-hover:bg-background transition-colors", children: item.icon }),
1821
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: item.label })
1822
- ]
1823
- },
1824
- i
1825
- )) })
1826
- ] }),
1827
- messages.map((message, i) => /* @__PURE__ */ jsx(
1828
- "div",
1829
- {
1830
- className: cn(
1831
- "flex flex-col",
1832
- message.role === "user" ? "items-end" : "items-start"
1953
+ const showStarterPrompts = messages.length === 0 && starterPrompts && starterPrompts.length > 0;
1954
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative flex flex-col h-full bg-background", className), children: [
1955
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 pb-36", children: [
1956
+ /* @__PURE__ */ jsxs(
1957
+ "div",
1958
+ {
1959
+ className: cn(
1960
+ "max-w-4xl mx-auto w-full p-4",
1961
+ showStarterPrompts && "min-h-full flex flex-col"
1962
+ ),
1963
+ children: [
1964
+ showStarterPrompts && /* @__PURE__ */ jsx(
1965
+ StarterPrompts,
1966
+ {
1967
+ prompts: starterPrompts,
1968
+ onPromptClick: handleStarterPromptClick
1969
+ }
1833
1970
  ),
1834
- children: /* @__PURE__ */ jsx(
1835
- "div",
1971
+ /* @__PURE__ */ jsx(
1972
+ MessageList,
1836
1973
  {
1837
- className: cn(
1838
- "max-w-[85%] rounded-2xl px-4 py-2 space-y-2",
1839
- message.role === "user" ? "bg-primary text-primary-foreground" : "px-0 py-0 text-foreground"
1840
- ),
1841
- children: message.content.map((event, j) => {
1842
- if (event.type === "text-delta")
1843
- return /* @__PURE__ */ jsx("span", { children: event.data?.delta }, j);
1844
- if (event.type === "text")
1845
- return /* @__PURE__ */ jsx("p", { children: event.data?.content || event.data?.text }, j);
1846
- if (event.ui) return /* @__PURE__ */ jsx(UIRenderer, { node: event.ui }, j);
1847
- return null;
1848
- })
1974
+ messages,
1975
+ isLoading,
1976
+ error
1849
1977
  }
1850
1978
  )
1851
- },
1852
- i
1853
- )),
1854
- isLoading && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground animate-pulse", children: "Thinking..." }),
1855
- error && /* @__PURE__ */ jsx("div", { className: "text-destructive p-2 border border-destructive rounded-md bg-destructive/10", children: error.message })
1856
- ] }),
1979
+ ]
1980
+ }
1981
+ ),
1857
1982
  /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1858
1983
  ] }),
1859
- /* @__PURE__ */ jsx("div", { className: "p-4 border-t w-full", children: /* @__PURE__ */ jsx("div", { className: "max-w-4xl mx-auto", children: /* @__PURE__ */ jsx(
1984
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 p-4 w-full", children: /* @__PURE__ */ jsx("div", { className: "max-w-4xl mx-auto", children: /* @__PURE__ */ jsx(
1860
1985
  Composer,
1861
1986
  {
1862
1987
  value: input,
@@ -1868,18 +1993,34 @@ function Thread({
1868
1993
  ) }) })
1869
1994
  ] });
1870
1995
  }
1996
+ function ChatHeader({
1997
+ title,
1998
+ leftContent,
1999
+ rightContent,
2000
+ className,
2001
+ titleClassName,
2002
+ children
2003
+ }) {
2004
+ if (children) {
2005
+ return /* @__PURE__ */ jsx("div", { className: cn("p-4 border-b border-border h-14 flex items-center shrink-0", className), children });
2006
+ }
2007
+ return /* @__PURE__ */ jsxs("div", { className: cn("p-4 border-b border-border h-14 flex items-center justify-between shrink-0", className), children: [
2008
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [
2009
+ leftContent,
2010
+ title && /* @__PURE__ */ jsx("div", { className: cn(
2011
+ "text-sm font-semibold truncate",
2012
+ typeof title === "string" ? titleClassName : ""
2013
+ ), children: title })
2014
+ ] }),
2015
+ rightContent && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0 ml-2", children: rightContent })
2016
+ ] });
2017
+ }
1871
2018
  var ThreadList = ({
1872
2019
  className,
1873
2020
  emptyState,
1874
2021
  onThreadSelect
1875
2022
  }) => {
1876
- const {
1877
- threads,
1878
- activeThreadId,
1879
- selectThread,
1880
- createThread,
1881
- deleteThread
1882
- } = useThreads();
2023
+ const { threads, activeThreadId, selectThread, createThread, deleteThread } = useThreads();
1883
2024
  const handleThreadClick = (threadId) => {
1884
2025
  if (threadId !== activeThreadId) {
1885
2026
  selectThread(threadId);
@@ -1917,10 +2058,10 @@ var ThreadList = ({
1917
2058
  return d.toLocaleDateString();
1918
2059
  };
1919
2060
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full", className), children: [
1920
- /* @__PURE__ */ jsx("div", { className: "p-2 border-b", children: /* @__PURE__ */ jsxs(
2061
+ /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsxs(
1921
2062
  Button,
1922
2063
  {
1923
- variant: "outline",
2064
+ variant: "ghost",
1924
2065
  size: "sm",
1925
2066
  onClick: handleNewThread,
1926
2067
  className: "w-full justify-start",
@@ -1941,31 +2082,13 @@ var ThreadList = ({
1941
2082
  {
1942
2083
  onClick: () => handleThreadClick(thread.id),
1943
2084
  className: cn(
1944
- "group relative flex items-center gap-3 p-3 rounded-lg cursor-pointer transition-colors",
1945
- isActive ? "bg-primary text-primary-foreground" : "hover:bg-muted"
2085
+ "group relative flex items-center gap-3 px-3 py-1.5 rounded-lg cursor-pointer transition-colors",
2086
+ isActive ? "bg-muted" : "hover:bg-muted"
1946
2087
  ),
1947
2088
  children: [
1948
2089
  /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1949
- /* @__PURE__ */ jsx(
1950
- "p",
1951
- {
1952
- className: cn(
1953
- "text-sm font-medium truncate",
1954
- isActive && "text-primary-foreground"
1955
- ),
1956
- children: thread.title || `Thread ${thread.id.slice(0, 8)}`
1957
- }
1958
- ),
1959
- thread.updatedAt && /* @__PURE__ */ jsx(
1960
- "span",
1961
- {
1962
- className: cn(
1963
- "text-xs shrink-0",
1964
- isActive ? "text-primary-foreground/70" : "text-muted-foreground"
1965
- ),
1966
- children: formatDate(thread.updatedAt)
1967
- }
1968
- )
2090
+ /* @__PURE__ */ jsx("p", { className: cn("text-sm font-medium truncate"), children: thread.title || `Thread ${thread.id.slice(0, 8)}` }),
2091
+ thread.updatedAt && /* @__PURE__ */ jsx("span", { className: cn("text-xs shrink-0"), children: formatDate(thread.updatedAt) })
1969
2092
  ] }) }),
1970
2093
  /* @__PURE__ */ jsx(
1971
2094
  Button,
@@ -1991,7 +2114,8 @@ function ChatPopup({
1991
2114
  title = "Chat",
1992
2115
  placeholder = "Message the AI",
1993
2116
  starterPrompts,
1994
- defaultOpen = false
2117
+ defaultOpen = false,
2118
+ headerProps
1995
2119
  }) {
1996
2120
  const [isOpen, setIsOpen] = useState(defaultOpen);
1997
2121
  const [view, setView] = useState("chat");
@@ -2006,9 +2130,11 @@ function ChatPopup({
2006
2130
  };
2007
2131
  return /* @__PURE__ */ jsxs("div", { className: "fixed bottom-6 right-6 z-50 flex flex-col items-end gap-4 font-sans", children: [
2008
2132
  isOpen && /* @__PURE__ */ jsxs(Card, { className: "py-0 w-[440px] h-[640px] flex flex-col overflow-hidden border bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60 shadow-2xl animate-in fade-in zoom-in-95 duration-200 origin-bottom-right", children: [
2009
- /* @__PURE__ */ jsxs(CardHeader, { className: "p-4 border-b flex flex-row items-center justify-between space-y-0 h-14 shrink-0", children: [
2010
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2011
- view === "history" && /* @__PURE__ */ jsx(
2133
+ /* @__PURE__ */ jsx(
2134
+ ChatHeader,
2135
+ {
2136
+ title: view === "history" ? "History" : title,
2137
+ leftContent: view === "history" ? /* @__PURE__ */ jsx(
2012
2138
  Button,
2013
2139
  {
2014
2140
  variant: "ghost",
@@ -2017,44 +2143,44 @@ function ChatPopup({
2017
2143
  className: "text-muted-foreground hover:text-foreground",
2018
2144
  children: /* @__PURE__ */ jsx(IconArrowLeft, { className: "size-4" })
2019
2145
  }
2020
- ),
2021
- /* @__PURE__ */ jsx(CardTitle, { className: "text-sm font-semibold", children: view === "history" ? "History" : title })
2022
- ] }),
2023
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2024
- view === "chat" && /* @__PURE__ */ jsx(
2025
- Button,
2026
- {
2027
- variant: "ghost",
2028
- size: "icon-xs",
2029
- onClick: () => setView("history"),
2030
- className: "text-muted-foreground hover:text-foreground",
2031
- title: "History",
2032
- children: /* @__PURE__ */ jsx(IconHistory, { className: "size-4" })
2033
- }
2034
- ),
2035
- /* @__PURE__ */ jsx(
2036
- Button,
2037
- {
2038
- variant: "ghost",
2039
- size: "icon-xs",
2040
- onClick: handleNewChat,
2041
- className: "text-muted-foreground hover:text-foreground",
2042
- title: "New Chat",
2043
- children: /* @__PURE__ */ jsx(IconPlus, { className: "size-4" })
2044
- }
2045
- ),
2046
- /* @__PURE__ */ jsx(
2047
- Button,
2048
- {
2049
- variant: "ghost",
2050
- size: "icon-xs",
2051
- onClick: () => setIsOpen(false),
2052
- className: "text-muted-foreground hover:text-foreground",
2053
- children: /* @__PURE__ */ jsx(IconX, { className: "size-4" })
2054
- }
2055
- )
2056
- ] })
2057
- ] }),
2146
+ ) : void 0,
2147
+ rightContent: /* @__PURE__ */ jsxs(Fragment, { children: [
2148
+ view === "chat" && /* @__PURE__ */ jsx(
2149
+ Button,
2150
+ {
2151
+ variant: "ghost",
2152
+ size: "icon-xs",
2153
+ onClick: () => setView("history"),
2154
+ className: "text-muted-foreground hover:text-foreground",
2155
+ title: "History",
2156
+ children: /* @__PURE__ */ jsx(IconHistory, { className: "size-4" })
2157
+ }
2158
+ ),
2159
+ /* @__PURE__ */ jsx(
2160
+ Button,
2161
+ {
2162
+ variant: "ghost",
2163
+ size: "icon-xs",
2164
+ onClick: handleNewChat,
2165
+ className: "text-muted-foreground hover:text-foreground",
2166
+ title: "New Chat",
2167
+ children: /* @__PURE__ */ jsx(IconPlus, { className: "size-4" })
2168
+ }
2169
+ ),
2170
+ /* @__PURE__ */ jsx(
2171
+ Button,
2172
+ {
2173
+ variant: "ghost",
2174
+ size: "icon-xs",
2175
+ onClick: () => setIsOpen(false),
2176
+ className: "text-muted-foreground hover:text-foreground",
2177
+ children: /* @__PURE__ */ jsx(IconX, { className: "size-4" })
2178
+ }
2179
+ )
2180
+ ] }),
2181
+ ...headerProps
2182
+ }
2183
+ ),
2058
2184
  /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: view === "chat" ? /* @__PURE__ */ jsx(
2059
2185
  Thread,
2060
2186
  {
@@ -2088,10 +2214,11 @@ function ChatSidebar({
2088
2214
  title = "Chat",
2089
2215
  placeholder = "Message the AI",
2090
2216
  starterPrompts,
2091
- className
2217
+ className,
2218
+ headerProps
2092
2219
  }) {
2093
2220
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full border-r bg-background w-80", className), children: [
2094
- /* @__PURE__ */ jsx("div", { className: "p-4 border-b h-14 flex items-center shrink-0", children: /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: title }) }),
2221
+ /* @__PURE__ */ jsx(ChatHeader, { title, ...headerProps }),
2095
2222
  /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
2096
2223
  Thread,
2097
2224
  {
@@ -2106,11 +2233,122 @@ function ChatFull({
2106
2233
  title = "Chat",
2107
2234
  placeholder = "Message the AI",
2108
2235
  starterPrompts,
2109
- className
2236
+ className,
2237
+ headerProps,
2238
+ leftSidebar,
2239
+ rightSidebar,
2240
+ leftSidebarClassName,
2241
+ rightSidebarClassName,
2242
+ leftSidebarCollapsible = false,
2243
+ rightSidebarCollapsible = false,
2244
+ defaultLeftSidebarCollapsed = false,
2245
+ defaultRightSidebarCollapsed = false,
2246
+ leftSidebarCollapsed: controlledLeftCollapsed,
2247
+ rightSidebarCollapsed: controlledRightCollapsed,
2248
+ onLeftSidebarCollapseChange,
2249
+ onRightSidebarCollapseChange
2110
2250
  }) {
2251
+ const [internalLeftCollapsed, setInternalLeftCollapsed] = useState(
2252
+ defaultLeftSidebarCollapsed
2253
+ );
2254
+ const [internalRightCollapsed, setInternalRightCollapsed] = useState(
2255
+ defaultRightSidebarCollapsed
2256
+ );
2257
+ const leftCollapsed = controlledLeftCollapsed !== void 0 ? controlledLeftCollapsed : internalLeftCollapsed;
2258
+ const rightCollapsed = controlledRightCollapsed !== void 0 ? controlledRightCollapsed : internalRightCollapsed;
2259
+ const handleLeftToggle = () => {
2260
+ const newCollapsed = !leftCollapsed;
2261
+ if (controlledLeftCollapsed === void 0) {
2262
+ setInternalLeftCollapsed(newCollapsed);
2263
+ }
2264
+ onLeftSidebarCollapseChange?.(newCollapsed);
2265
+ };
2266
+ const handleRightToggle = () => {
2267
+ const newCollapsed = !rightCollapsed;
2268
+ if (controlledRightCollapsed === void 0) {
2269
+ setInternalRightCollapsed(newCollapsed);
2270
+ }
2271
+ onRightSidebarCollapseChange?.(newCollapsed);
2272
+ };
2111
2273
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full w-full bg-background", className), children: [
2112
- title && /* @__PURE__ */ jsx("div", { className: "p-4 border-b h-14 flex items-center shrink-0", children: /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: title }) }),
2113
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(Thread, { placeholder, starterPrompts }) })
2274
+ title && /* @__PURE__ */ jsx(ChatHeader, { title, ...headerProps }),
2275
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden flex relative", children: [
2276
+ leftSidebar && /* @__PURE__ */ jsxs(Fragment, { children: [
2277
+ /* @__PURE__ */ jsx(
2278
+ "div",
2279
+ {
2280
+ className: cn(
2281
+ "flex-shrink-0 border-r border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
2282
+ leftCollapsed && leftSidebarCollapsible ? "w-0 border-r-0 min-w-0" : "",
2283
+ !leftCollapsed && leftSidebarClassName
2284
+ ),
2285
+ children: !leftCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
2286
+ leftSidebarCollapsible && /* @__PURE__ */ jsx("div", { className: "flex justify-end p-2 border-b border-border shrink-0", children: /* @__PURE__ */ jsx(
2287
+ Button,
2288
+ {
2289
+ variant: "ghost",
2290
+ size: "icon-sm",
2291
+ onClick: handleLeftToggle,
2292
+ "aria-label": "Collapse left sidebar",
2293
+ className: "h-8 w-8",
2294
+ children: /* @__PURE__ */ jsx(IconChevronLeft, { className: "h-4 w-4" })
2295
+ }
2296
+ ) }),
2297
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: leftSidebar })
2298
+ ] })
2299
+ }
2300
+ ),
2301
+ leftSidebarCollapsible && leftCollapsed && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 border-r border-border bg-background flex items-center justify-center w-10", children: /* @__PURE__ */ jsx(
2302
+ Button,
2303
+ {
2304
+ variant: "ghost",
2305
+ size: "icon-sm",
2306
+ onClick: handleLeftToggle,
2307
+ "aria-label": "Expand left sidebar",
2308
+ className: "h-8 w-8",
2309
+ children: /* @__PURE__ */ jsx(IconChevronRight, { className: "h-4 w-4" })
2310
+ }
2311
+ ) })
2312
+ ] }),
2313
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-w-0", children: /* @__PURE__ */ jsx(Thread, { placeholder, starterPrompts }) }),
2314
+ rightSidebar && /* @__PURE__ */ jsxs(Fragment, { children: [
2315
+ rightSidebarCollapsible && rightCollapsed && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 border-l border-border bg-background flex items-center justify-center w-10", children: /* @__PURE__ */ jsx(
2316
+ Button,
2317
+ {
2318
+ variant: "ghost",
2319
+ size: "icon-sm",
2320
+ onClick: handleRightToggle,
2321
+ "aria-label": "Expand right sidebar",
2322
+ className: "h-8 w-8",
2323
+ children: /* @__PURE__ */ jsx(IconChevronLeft, { className: "h-4 w-4" })
2324
+ }
2325
+ ) }),
2326
+ /* @__PURE__ */ jsx(
2327
+ "div",
2328
+ {
2329
+ className: cn(
2330
+ "flex-shrink-0 border-l border-border bg-background transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
2331
+ rightCollapsed && rightSidebarCollapsible ? "w-0 border-l-0 min-w-0" : "",
2332
+ !rightCollapsed && rightSidebarClassName
2333
+ ),
2334
+ children: !rightCollapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
2335
+ rightSidebarCollapsible && /* @__PURE__ */ jsx("div", { className: "flex justify-start p-2 border-b border-border shrink-0", children: /* @__PURE__ */ jsx(
2336
+ Button,
2337
+ {
2338
+ variant: "ghost",
2339
+ size: "icon-sm",
2340
+ onClick: handleRightToggle,
2341
+ "aria-label": "Collapse right sidebar",
2342
+ className: "h-8 w-8",
2343
+ children: /* @__PURE__ */ jsx(IconChevronRight, { className: "h-4 w-4" })
2344
+ }
2345
+ ) }),
2346
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden min-h-0", children: rightSidebar })
2347
+ ] })
2348
+ }
2349
+ )
2350
+ ] })
2351
+ ] })
2114
2352
  ] });
2115
2353
  }
2116
2354
  function AlertDialog({ ...props }) {
@@ -2254,8 +2492,8 @@ var AccountDialog = ({
2254
2492
  size
2255
2493
  }) => {
2256
2494
  const { isLoading, isAuthenticated, login, logout } = useAuth();
2257
- const [open, setOpen] = React9.useState(false);
2258
- const [error, setError] = React9.useState(null);
2495
+ const [open, setOpen] = React10.useState(false);
2496
+ const [error, setError] = React10.useState(null);
2259
2497
  const handleGoogleSignIn = async () => {
2260
2498
  login();
2261
2499
  };
@@ -2330,7 +2568,42 @@ var AccountDialog = ({
2330
2568
  ] }) })
2331
2569
  ] });
2332
2570
  };
2571
+ function ThemeToggle() {
2572
+ const { theme, setTheme, resolvedTheme } = useTheme();
2573
+ const cycleTheme = () => {
2574
+ if (theme === "light") {
2575
+ setTheme("dark");
2576
+ } else if (theme === "dark") {
2577
+ setTheme("system");
2578
+ } else {
2579
+ setTheme("light");
2580
+ }
2581
+ };
2582
+ const getIcon = () => {
2583
+ if (theme === "system") {
2584
+ return /* @__PURE__ */ jsx(IconDeviceDesktop, { className: "h-4 w-4" });
2585
+ }
2586
+ return resolvedTheme === "dark" ? /* @__PURE__ */ jsx(IconMoon, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(IconSun, { className: "h-4 w-4" });
2587
+ };
2588
+ const getLabel = () => {
2589
+ if (theme === "system") {
2590
+ return "System";
2591
+ }
2592
+ return resolvedTheme === "dark" ? "Dark" : "Light";
2593
+ };
2594
+ return /* @__PURE__ */ jsx(
2595
+ Button,
2596
+ {
2597
+ variant: "ghost",
2598
+ size: "icon",
2599
+ onClick: cycleTheme,
2600
+ "aria-label": `Toggle theme (current: ${getLabel()})`,
2601
+ title: `Current: ${getLabel()}. Click to cycle: Light \u2192 Dark \u2192 System`,
2602
+ children: getIcon()
2603
+ }
2604
+ );
2605
+ }
2333
2606
 
2334
- export { AccountDialog, AuthContext, AuthProvider, ChatFull, ChatPopup, ChatSidebar, Composer, MelonyContext, MelonyProvider, Thread, ThreadContext, ThreadList, ThreadProvider, UIRenderer, useAuth, useMelony, useThreads };
2607
+ export { AccountDialog, AuthContext, AuthProvider, ChatFull, ChatHeader, ChatPopup, ChatSidebar, Composer, MelonyClientProvider, MelonyContext, ThemeProvider, ThemeToggle, Thread, ThreadContext, ThreadList, ThreadProvider, UIRenderer, groupEventsToMessages, useAuth, useMelony, useTheme, useThreads };
2335
2608
  //# sourceMappingURL=index.js.map
2336
2609
  //# sourceMappingURL=index.js.map