@px-ui/ai 4.0.0 → 4.2.0

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.d.ts CHANGED
@@ -10,28 +10,83 @@ interface Message {
10
10
  type?: MessageType;
11
11
  debugTrace?: unknown;
12
12
  }
13
+ interface XandiApiResponse {
14
+ success: boolean;
15
+ message: string;
16
+ data: {
17
+ intent: string;
18
+ data: unknown;
19
+ conversation_id: string;
20
+ };
21
+ trace?: {
22
+ trace_id: string;
23
+ execution_mode: string;
24
+ intent: string;
25
+ tool_id: string;
26
+ debug_trace: unknown;
27
+ };
28
+ }
13
29
  interface XandiResponse {
14
30
  content: string;
15
31
  type?: MessageType;
16
32
  debugTrace?: unknown;
33
+ conversationId?: string;
17
34
  }
18
- interface XandiContextValue {
35
+ interface ConversationSummary {
36
+ id: string;
37
+ title: string;
38
+ timestamp: Date;
39
+ }
40
+ interface Conversation {
41
+ id: string | null;
42
+ title: string;
19
43
  messages: Message[];
44
+ createdAt: Date;
45
+ updatedAt: Date;
46
+ }
47
+ type XandiUIMode = "full" | "sidebar" | "floating";
48
+ interface XandiConfig {
49
+ avatarUrl?: string;
50
+ assistantName?: string;
51
+ uiMode?: XandiUIMode;
52
+ }
53
+ interface FetchRespOptions {
54
+ conversationId?: string;
55
+ signal?: AbortSignal;
56
+ }
57
+ interface GetConvOptions {
58
+ page?: number;
59
+ perPage?: number;
60
+ }
61
+ interface XandiHandlers {
62
+ fetchResp: (message: string, options?: FetchRespOptions) => Promise<XandiResponse>;
63
+ getConv?: (conversationId: string, options?: GetConvOptions) => Promise<Conversation>;
64
+ getConvHistory?: () => Promise<ConversationSummary[]>;
65
+ onFeedback?: (messageId: string, conversationId: string, feedback: FeedbackType) => void;
66
+ onStop?: (conversationId: string) => void;
67
+ }
68
+ interface XandiContextValue {
69
+ conversation: Conversation;
20
70
  isLoading: boolean;
21
- sessionId: string | null;
22
71
  sendMessage: (text: string) => void;
23
- onFeedback?: (messageId: string, feedback: FeedbackType) => void;
72
+ stopRequest: () => void;
73
+ loadConversation: (conversationId: string, options?: GetConvOptions) => Promise<void>;
74
+ startNewConversation: () => void;
75
+ submitFeedback: (messageId: string, feedback: FeedbackType) => void;
76
+ getConvHistory?: () => Promise<ConversationSummary[]>;
77
+ config: Required<XandiConfig>;
78
+ setUiModeOverride: (mode: XandiUIMode | null) => void;
24
79
  }
25
80
  interface XandiProviderProps {
26
- fetchResponse: (message: string) => Promise<XandiResponse>;
27
- sessionId?: string;
28
- onFeedback?: (messageId: string, feedback: FeedbackType) => void;
81
+ handlers: XandiHandlers;
82
+ conversationId?: string;
83
+ config?: XandiConfig;
29
84
  children: React.ReactNode;
30
85
  }
31
86
  declare function XandiProvider({
32
- fetchResponse,
33
- sessionId: initialSessionId,
34
- onFeedback,
87
+ handlers,
88
+ conversationId: initialConversationId,
89
+ config: userConfig,
35
90
  children
36
91
  }: XandiProviderProps): react_jsx_runtime0.JSX.Element;
37
92
  declare function useXandi(): XandiContextValue;
@@ -47,73 +102,55 @@ interface Suggestion {
47
102
  interface XandiProps {
48
103
  welcomeMessage?: string;
49
104
  suggestions?: Suggestion[];
105
+ uiMode?: XandiUIMode;
50
106
  }
51
107
  declare function Xandi({
52
108
  welcomeMessage,
53
- suggestions
109
+ suggestions,
110
+ uiMode
54
111
  }: XandiProps): react_jsx_runtime0.JSX.Element;
55
112
  //#endregion
56
113
  //#region src/components/x-header.d.ts
57
114
  interface XHeaderProps {
58
- title?: string;
59
115
  onClose?: () => void;
60
- onNewChat?: () => void;
61
116
  onToggleHistory?: () => void;
62
117
  }
63
118
  declare function XHeader({
64
- title,
65
119
  onClose,
66
- onNewChat,
67
120
  onToggleHistory
68
121
  }: XHeaderProps): react_jsx_runtime0.JSX.Element;
69
122
  //#endregion
123
+ //#region src/components/x-sidebar.d.ts
124
+ interface XSidebarProps {
125
+ isOpen?: boolean;
126
+ onClose?: () => void;
127
+ }
128
+ declare function XSidebar({
129
+ isOpen,
130
+ onClose
131
+ }: XSidebarProps): react_jsx_runtime0.JSX.Element | null;
132
+ //#endregion
70
133
  //#region src/components/x-chat-history.d.ts
71
134
  interface ChatHistoryItem {
72
135
  id: string;
73
136
  title: string;
74
137
  timestamp: Date;
75
138
  }
76
- interface ChatHistoryGroup {
77
- label: string;
78
- items: ChatHistoryItem[];
79
- }
80
139
  interface XChatHistoryProps {
81
- groups?: ChatHistoryGroup[];
140
+ items?: ChatHistoryItem[];
141
+ isLoading?: boolean;
82
142
  activeChatId?: string;
83
143
  onSelectChat?: (chatId: string) => void;
84
144
  }
85
145
  declare function XChatHistory({
86
- groups,
146
+ items,
147
+ isLoading,
87
148
  activeChatId,
88
149
  onSelectChat
89
150
  }: XChatHistoryProps): react_jsx_runtime0.JSX.Element;
90
- //#endregion
91
- //#region src/components/x-sidebar.d.ts
92
- interface XSidebarProps {
93
- isOpen?: boolean;
94
- chatHistory?: ChatHistoryGroup[];
95
- activeChatId?: string;
96
- onClose?: () => void;
97
- onNewChat?: () => void;
98
- onSelectChat?: (chatId: string) => void;
99
- }
100
- declare function XSidebar({
101
- isOpen,
102
- chatHistory,
103
- activeChatId,
104
- onClose,
105
- onNewChat,
106
- onSelectChat
107
- }: XSidebarProps): react_jsx_runtime0.JSX.Element | null;
108
151
  declare namespace x_message_actions_d_exports {
109
152
  export { Copy, CopyProps, Debug, DebugProps, Feedback, FeedbackProps, FeedbackType, Root };
110
153
  }
111
- /**
112
- * Container for message actions. Use with composable children:
113
- * - XMessageActions.Feedback
114
- * - XMessageActions.Copy
115
- * - XMessageActions.Debug
116
- */
117
154
  declare function Root({
118
155
  children
119
156
  }: {
@@ -140,5 +177,5 @@ declare function Debug({
140
177
  debugTrace
141
178
  }: DebugProps): react_jsx_runtime0.JSX.Element;
142
179
  //#endregion
143
- export { type ChatHistoryGroup, type ChatHistoryItem, type FeedbackType, type Message, type MessageType, type Suggestion, XChatHistory, type XChatHistoryProps, XHeader, type XHeaderProps, x_message_actions_d_exports as XMessageActions, XSidebar, type XSidebarProps, Xandi, type XandiProps, XandiProvider, type XandiProviderProps, type XandiResponse, useXandi };
180
+ export { type ChatHistoryItem, type Conversation, type ConversationSummary, type FeedbackType, type FetchRespOptions, type GetConvOptions, type Message, type MessageType, type Suggestion, XChatHistory, type XChatHistoryProps, XHeader, type XHeaderProps, x_message_actions_d_exports as XMessageActions, XSidebar, type XSidebarProps, Xandi, type XandiApiResponse, type XandiConfig, type XandiContextValue, type XandiHandlers, type XandiProps, XandiProvider, type XandiProviderProps, type XandiResponse, type XandiUIMode, useXandi };
144
181
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/context/xandi-context.tsx","../src/components/x-main-intake.tsx","../src/components/xandi.tsx","../src/components/x-header.tsx","../src/components/x-chat-history.tsx","../src/components/x-sidebar.tsx","../src/components/x-message-actions.tsx"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/context/xandi-context.tsx","../src/components/x-main-intake.tsx","../src/components/xandi.tsx","../src/components/x-header.tsx","../src/components/x-sidebar.tsx","../src/components/x-chat-history.tsx","../src/components/x-message-actions.tsx"],"sourcesContent":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,18 +1,71 @@
1
1
  import { t as __export } from "./chunk-BAz01cYq.js";
2
- import { createContext, useContext, useEffect, useRef, useState } from "react";
2
+ import { createContext, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
- import { Avatar, Button, Dialog, FileIcon, SendIcon, StopIcon, Tooltip, toast } from "@px-ui/core";
4
+ import { Avatar, Button, Dialog, FileIcon, SendIcon, Spinner, StopIcon, Tooltip, toast } from "@px-ui/core";
5
5
  import ReactMarkdown from "react-markdown";
6
6
 
7
+ //#region src/constants.ts
8
+ function getXandiAvatarUrl() {
9
+ try {
10
+ return new URL("./assets/images/xandi-avatar.png", import.meta.url).href;
11
+ } catch {
12
+ return "";
13
+ }
14
+ }
15
+ const XANDI_AVATAR_URL = getXandiAvatarUrl();
16
+
17
+ //#endregion
7
18
  //#region src/context/xandi-context.tsx
19
+ function createEmptyConversation() {
20
+ return {
21
+ id: null,
22
+ title: "New Chat",
23
+ messages: [],
24
+ createdAt: /* @__PURE__ */ new Date(),
25
+ updatedAt: /* @__PURE__ */ new Date()
26
+ };
27
+ }
28
+ const defaultConfig = {
29
+ avatarUrl: XANDI_AVATAR_URL,
30
+ assistantName: "Xandi",
31
+ uiMode: "full"
32
+ };
33
+ const defaultGetConvOptions = {
34
+ page: 1,
35
+ perPage: 20
36
+ };
8
37
  const XandiContext = createContext(null);
9
- function XandiProvider({ fetchResponse, sessionId: initialSessionId, onFeedback, children }) {
10
- const [sessionId, setSessionId] = useState(initialSessionId ?? null);
11
- const [messages, setMessages] = useState([]);
38
+ function XandiProvider({ handlers, conversationId: initialConversationId, config: userConfig, children }) {
39
+ const [conversation, setConversation] = useState(createEmptyConversation);
12
40
  const [isLoading, setIsLoading] = useState(false);
41
+ const [uiModeOverride, setUiModeOverride] = useState(null);
42
+ const abortControllerRef = useRef(null);
43
+ const config = {
44
+ ...defaultConfig,
45
+ ...userConfig,
46
+ uiMode: uiModeOverride ?? userConfig?.uiMode ?? defaultConfig.uiMode
47
+ };
13
48
  useEffect(() => {
14
- if (!initialSessionId) setSessionId(crypto.randomUUID());
15
- }, [initialSessionId]);
49
+ if (initialConversationId && handlers.getConv) loadConversation(initialConversationId);
50
+ }, [initialConversationId]);
51
+ const loadConversation = async (convId, options) => {
52
+ if (!handlers.getConv) return;
53
+ const opts = {
54
+ ...defaultGetConvOptions,
55
+ ...options
56
+ };
57
+ try {
58
+ setIsLoading(true);
59
+ setConversation(await handlers.getConv(convId, opts));
60
+ } catch (error) {
61
+ console.error("Failed to load conversation:", error);
62
+ } finally {
63
+ setIsLoading(false);
64
+ }
65
+ };
66
+ const startNewConversation = () => {
67
+ setConversation(createEmptyConversation());
68
+ };
16
69
  const sendMessage = async (text) => {
17
70
  if (!text.trim() || isLoading) return;
18
71
  const userMessage = {
@@ -20,10 +73,18 @@ function XandiProvider({ fetchResponse, sessionId: initialSessionId, onFeedback,
20
73
  role: "user",
21
74
  content: text
22
75
  };
23
- setMessages((prev) => [...prev, userMessage]);
76
+ setConversation((prev) => ({
77
+ ...prev,
78
+ messages: [...prev.messages, userMessage],
79
+ updatedAt: /* @__PURE__ */ new Date()
80
+ }));
24
81
  setIsLoading(true);
82
+ abortControllerRef.current = new AbortController();
25
83
  try {
26
- const response = await fetchResponse(text);
84
+ const response = await handlers.fetchResp(text, {
85
+ conversationId: conversation.id ?? void 0,
86
+ signal: abortControllerRef.current.signal
87
+ });
27
88
  const assistantMessage = {
28
89
  id: crypto.randomUUID(),
29
90
  role: "assistant",
@@ -31,19 +92,41 @@ function XandiProvider({ fetchResponse, sessionId: initialSessionId, onFeedback,
31
92
  type: response.type,
32
93
  debugTrace: response.debugTrace
33
94
  };
34
- setMessages((prev) => [...prev, assistantMessage]);
95
+ setConversation((prev) => ({
96
+ ...prev,
97
+ id: response.conversationId ?? prev.id,
98
+ messages: [...prev.messages, assistantMessage],
99
+ updatedAt: /* @__PURE__ */ new Date()
100
+ }));
35
101
  } catch (error) {
36
- console.error("Failed to send message:", error);
102
+ if (error.name !== "AbortError") console.error("Failed to send message:", error);
37
103
  } finally {
38
104
  setIsLoading(false);
105
+ abortControllerRef.current = null;
106
+ }
107
+ };
108
+ const stopRequest = () => {
109
+ if (abortControllerRef.current) {
110
+ abortControllerRef.current.abort();
111
+ abortControllerRef.current = null;
39
112
  }
113
+ if (conversation.id && handlers.onStop) handlers.onStop(conversation.id);
114
+ setIsLoading(false);
115
+ };
116
+ const submitFeedback = (messageId, feedback) => {
117
+ if (handlers.onFeedback && conversation.id) handlers.onFeedback(messageId, conversation.id, feedback);
40
118
  };
41
119
  const value = {
42
- messages,
120
+ conversation,
43
121
  isLoading,
44
- sessionId,
45
122
  sendMessage,
46
- onFeedback
123
+ stopRequest,
124
+ loadConversation,
125
+ startNewConversation,
126
+ submitFeedback,
127
+ getConvHistory: handlers.getConvHistory,
128
+ config,
129
+ setUiModeOverride
47
130
  };
48
131
  return /* @__PURE__ */ jsx(XandiContext.Provider, {
49
132
  value,
@@ -59,7 +142,7 @@ function useXandi() {
59
142
  //#endregion
60
143
  //#region src/components/x-main-intake.tsx
61
144
  function XMainIntake({ placeholder = "Ask about jobs, candidates, timesheets, or anything workforce...", suggestions = [] }) {
62
- const { isLoading, sendMessage } = useXandi();
145
+ const { isLoading, sendMessage, stopRequest } = useXandi();
63
146
  const [input, setInput] = useState("");
64
147
  const handleSubmit = (e) => {
65
148
  e?.preventDefault();
@@ -71,6 +154,9 @@ function XMainIntake({ placeholder = "Ask about jobs, candidates, timesheets, or
71
154
  const handleSuggestionClick = (prompt) => {
72
155
  setInput(prompt);
73
156
  };
157
+ const handleStopOrSend = () => {
158
+ if (isLoading) stopRequest();
159
+ };
74
160
  return /* @__PURE__ */ jsxs("div", {
75
161
  className: "flex flex-col gap-3",
76
162
  children: [/* @__PURE__ */ jsxs("form", {
@@ -99,12 +185,18 @@ function XMainIntake({ placeholder = "Ask about jobs, candidates, timesheets, or
99
185
  className: "ml-auto text-ppx-xs text-ppx-neutral-10",
100
186
  children: "Enter to send · Shift+Enter for new line"
101
187
  }),
102
- /* @__PURE__ */ jsx(Button, {
188
+ isLoading ? /* @__PURE__ */ jsx(Button, {
189
+ type: "button",
190
+ size: "icon-sm",
191
+ onClick: handleStopOrSend,
192
+ className: "flex h-8 w-8 items-center justify-center rounded-full bg-ppx-red-5 text-white transition-all hover:bg-ppx-red-4 hover:shadow-[0_0_12px_rgba(220,38,38,0.6)]",
193
+ children: /* @__PURE__ */ jsx(StopIcon, { width: 14 })
194
+ }) : /* @__PURE__ */ jsx(Button, {
103
195
  type: "submit",
104
196
  size: "icon-sm",
105
- disabled: isLoading || !input.trim(),
197
+ disabled: !input.trim(),
106
198
  className: "flex h-8 w-8 items-center justify-center rounded-full bg-ppx-green-5 text-white transition-all hover:bg-ppx-green-4 hover:shadow-[0_0_12px_rgba(40,182,116,0.6)] disabled:bg-ppx-neutral-5 disabled:text-ppx-neutral-10 disabled:shadow-none",
107
- children: isLoading ? /* @__PURE__ */ jsx(StopIcon, { width: 14 }) : /* @__PURE__ */ jsx(SendIcon, { width: 16 })
199
+ children: /* @__PURE__ */ jsx(SendIcon, { width: 16 })
108
200
  })
109
201
  ]
110
202
  })
@@ -351,12 +443,6 @@ var x_message_actions_exports = /* @__PURE__ */ __export({
351
443
  Feedback: () => Feedback,
352
444
  Root: () => Root
353
445
  });
354
- /**
355
- * Container for message actions. Use with composable children:
356
- * - XMessageActions.Feedback
357
- * - XMessageActions.Copy
358
- * - XMessageActions.Debug
359
- */
360
446
  function Root({ children }) {
361
447
  return /* @__PURE__ */ jsx("div", {
362
448
  className: "flex items-center gap-1",
@@ -364,12 +450,12 @@ function Root({ children }) {
364
450
  });
365
451
  }
366
452
  function Feedback({ messageId }) {
367
- const { onFeedback } = useXandi();
453
+ const { submitFeedback } = useXandi();
368
454
  const [feedback, setFeedback] = useState(null);
369
455
  const handleFeedback = (type) => {
370
456
  const newFeedback = feedback === type ? null : type;
371
457
  setFeedback(newFeedback);
372
- onFeedback?.(messageId, newFeedback);
458
+ submitFeedback(messageId, newFeedback);
373
459
  };
374
460
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Tooltip.Root, { children: [/* @__PURE__ */ jsx(Tooltip.Trigger, { render: /* @__PURE__ */ jsx(Button, {
375
461
  variant: "ghost",
@@ -519,10 +605,6 @@ function TextRenderer({ message }) {
519
605
 
520
606
  //#endregion
521
607
  //#region src/components/x-message-item.tsx
522
- /**
523
- * Router component that renders messages based on their type.
524
- * Defaults to markdown rendering if no type is specified.
525
- */
526
608
  function XMessageItem({ message }) {
527
609
  const isUser = message.role === "user";
528
610
  const messageType = message.type ?? "markdown";
@@ -543,9 +625,6 @@ function XMessageItem({ message }) {
543
625
  })
544
626
  });
545
627
  }
546
- /**
547
- * Switch component that selects the appropriate renderer based on message type.
548
- */
549
628
  function MessageRenderer({ type, message }) {
550
629
  switch (type) {
551
630
  case "text": return /* @__PURE__ */ jsx(TextRenderer, { message });
@@ -554,27 +633,17 @@ function MessageRenderer({ type, message }) {
554
633
  }
555
634
  }
556
635
 
557
- //#endregion
558
- //#region src/constants.ts
559
- function getXandiAvatarUrl() {
560
- try {
561
- return new URL("./assets/images/xandi-avatar.png", import.meta.url).href;
562
- } catch {
563
- return "";
564
- }
565
- }
566
- const XANDI_AVATAR_URL = getXandiAvatarUrl();
567
-
568
636
  //#endregion
569
637
  //#region src/components/x-typing-indicator.tsx
570
638
  function XTypingIndicator() {
639
+ const { config } = useXandi();
571
640
  return /* @__PURE__ */ jsxs("div", {
572
641
  className: "flex items-center gap-4",
573
642
  children: [/* @__PURE__ */ jsx("div", {
574
643
  className: "animate-[popUp_0.3s_ease-out_forwards]",
575
644
  children: /* @__PURE__ */ jsx(Avatar, {
576
- imgSrc: XANDI_AVATAR_URL,
577
- name: "Xandi",
645
+ imgSrc: config.avatarUrl,
646
+ name: config.assistantName,
578
647
  variant: "rounded",
579
648
  size: "48px",
580
649
  hideTooltip: true,
@@ -593,19 +662,79 @@ function XTypingIndicator() {
593
662
 
594
663
  //#endregion
595
664
  //#region src/components/x-message-container.tsx
596
- function XMessageContainer({ height = 400 }) {
597
- const { messages, isLoading } = useXandi();
665
+ function XMessageContainer({ height = 400, onLoadMore }) {
666
+ const { conversation, isLoading } = useXandi();
598
667
  const containerRef = useRef(null);
668
+ const topSentinelRef = useRef(null);
669
+ const prevScrollHeightRef = useRef(0);
670
+ const prevScrollTopRef = useRef(0);
671
+ const prevLastMessageIdRef = useRef(null);
672
+ const prevMessageCountRef = useRef(0);
673
+ const skipScrollToBottomRef = useRef(false);
674
+ const messages = conversation.messages;
675
+ const messageCount = messages.length;
676
+ const lastMessageId = messageCount > 0 ? messages[messageCount - 1].id : null;
677
+ useLayoutEffect(() => {
678
+ const el = containerRef.current;
679
+ if (!el) return;
680
+ const prevCount = prevMessageCountRef.current;
681
+ const prevLastId = prevLastMessageIdRef.current;
682
+ if (prevCount > 0 && messageCount > prevCount && lastMessageId != null && lastMessageId === prevLastId) {
683
+ const prevScrollHeight = prevScrollHeightRef.current;
684
+ const prevScrollTop = prevScrollTopRef.current;
685
+ const newScrollHeight = el.scrollHeight;
686
+ el.scrollTop = Math.max(0, prevScrollTop + (newScrollHeight - prevScrollHeight));
687
+ skipScrollToBottomRef.current = true;
688
+ }
689
+ prevScrollHeightRef.current = el.scrollHeight;
690
+ prevScrollTopRef.current = el.scrollTop;
691
+ prevMessageCountRef.current = messageCount;
692
+ prevLastMessageIdRef.current = lastMessageId;
693
+ }, [
694
+ messageCount,
695
+ lastMessageId,
696
+ messages
697
+ ]);
698
+ useEffect(() => {
699
+ const el = containerRef.current;
700
+ if (!el) return;
701
+ if (skipScrollToBottomRef.current) {
702
+ skipScrollToBottomRef.current = false;
703
+ return;
704
+ }
705
+ el.scrollTop = el.scrollHeight - el.clientHeight;
706
+ }, [conversation.messages, isLoading]);
599
707
  useEffect(() => {
600
- if (containerRef.current) containerRef.current.scrollTop = containerRef.current.scrollHeight;
601
- }, [messages, isLoading]);
708
+ if (!onLoadMore) return;
709
+ const sentinel = topSentinelRef.current;
710
+ const container = containerRef.current;
711
+ if (!sentinel || !container) return;
712
+ const observer = new IntersectionObserver((entries) => {
713
+ const [entry] = entries;
714
+ if (entry?.isIntersecting) onLoadMore();
715
+ }, {
716
+ root: container,
717
+ rootMargin: "0px",
718
+ threshold: 0
719
+ });
720
+ observer.observe(sentinel);
721
+ return () => observer.disconnect();
722
+ }, [onLoadMore]);
602
723
  return /* @__PURE__ */ jsx("div", {
603
724
  ref: containerRef,
604
- className: "overflow-y-auto py-[10px]",
725
+ className: "flex flex-col overflow-y-auto py-[10px]",
605
726
  style: { height: typeof height === "number" ? `${height}px` : height },
606
727
  children: /* @__PURE__ */ jsxs("div", {
607
- className: "flex flex-col gap-5 p-4",
608
- children: [messages.map((message) => /* @__PURE__ */ jsx(XMessageItem, { message }, message.id)), isLoading && /* @__PURE__ */ jsx(XTypingIndicator, {})]
728
+ className: "flex flex-col-reverse gap-5 p-4",
729
+ children: [
730
+ isLoading && /* @__PURE__ */ jsx(XTypingIndicator, {}),
731
+ [...messages].reverse().map((message) => /* @__PURE__ */ jsx(XMessageItem, { message }, message.id)),
732
+ /* @__PURE__ */ jsx("div", {
733
+ ref: topSentinelRef,
734
+ className: "h-1 shrink-0",
735
+ "aria-hidden": "true"
736
+ })
737
+ ]
609
738
  })
610
739
  });
611
740
  }
@@ -613,6 +742,7 @@ function XMessageContainer({ height = 400 }) {
613
742
  //#endregion
614
743
  //#region src/components/x-welcome.tsx
615
744
  function XWelcome({ message }) {
745
+ const { config } = useXandi();
616
746
  return /* @__PURE__ */ jsxs("div", {
617
747
  className: "flex flex-col items-center justify-center gap-4 py-12",
618
748
  children: [/* @__PURE__ */ jsxs("div", {
@@ -622,8 +752,8 @@ function XWelcome({ message }) {
622
752
  /* @__PURE__ */ jsx("div", {
623
753
  className: "relative rounded-full bg-ppx-neutral-18 p-1",
624
754
  children: /* @__PURE__ */ jsx(Avatar, {
625
- imgSrc: XANDI_AVATAR_URL,
626
- name: "Xandi",
755
+ imgSrc: config.avatarUrl,
756
+ name: config.assistantName,
627
757
  variant: "rounded",
628
758
  size: "120px",
629
759
  hideTooltip: true
@@ -646,9 +776,13 @@ function XWelcome({ message }) {
646
776
 
647
777
  //#endregion
648
778
  //#region src/components/xandi.tsx
649
- function Xandi({ welcomeMessage = "How can I help you today?", suggestions = [] }) {
650
- const { messages, isLoading } = useXandi();
651
- const isEmpty = messages.length === 0;
779
+ function Xandi({ welcomeMessage = "How can I help you today?", suggestions = [], uiMode }) {
780
+ const { conversation, setUiModeOverride } = useXandi();
781
+ useEffect(() => {
782
+ setUiModeOverride(uiMode ?? null);
783
+ return () => setUiModeOverride(null);
784
+ }, [uiMode, setUiModeOverride]);
785
+ const isEmpty = conversation.messages.length === 0;
652
786
  return /* @__PURE__ */ jsxs("div", {
653
787
  className: "flex flex-col",
654
788
  children: [isEmpty ? /* @__PURE__ */ jsx(XWelcome, { message: welcomeMessage }) : /* @__PURE__ */ jsx(XMessageContainer, {}), /* @__PURE__ */ jsx(XMainIntake, { suggestions: isEmpty ? suggestions : [] })]
@@ -657,9 +791,10 @@ function Xandi({ welcomeMessage = "How can I help you today?", suggestions = []
657
791
 
658
792
  //#endregion
659
793
  //#region src/components/x-header.tsx
660
- function XHeader({ title = "Xandi", onClose, onNewChat, onToggleHistory }) {
794
+ function XHeader({ onClose, onToggleHistory }) {
795
+ const { startNewConversation, config } = useXandi();
661
796
  return /* @__PURE__ */ jsxs("header", {
662
- className: "flex items-center justify-between border-b border-ppx-neutral-5 bg-ppx-neutral-2 px-3 py-2",
797
+ className: "flex items-center justify-between border-b border-ppx-neutral-5 bg-transparent px-3 py-2",
663
798
  children: [/* @__PURE__ */ jsxs("div", {
664
799
  className: "flex items-center gap-2",
665
800
  children: [/* @__PURE__ */ jsx(Button, {
@@ -671,14 +806,14 @@ function XHeader({ title = "Xandi", onClose, onNewChat, onToggleHistory }) {
671
806
  }), /* @__PURE__ */ jsxs("div", {
672
807
  className: "flex items-center gap-2",
673
808
  children: [/* @__PURE__ */ jsx(Avatar, {
674
- imgSrc: XANDI_AVATAR_URL,
675
- name: "Xandi",
809
+ imgSrc: config.avatarUrl,
810
+ name: config.assistantName,
676
811
  variant: "rounded",
677
812
  size: "24px",
678
813
  hideTooltip: true
679
814
  }), /* @__PURE__ */ jsx("span", {
680
815
  className: "font-medium text-ppx-foreground",
681
- children: title
816
+ children: config.assistantName
682
817
  })]
683
818
  })]
684
819
  }), /* @__PURE__ */ jsxs("div", {
@@ -686,10 +821,10 @@ function XHeader({ title = "Xandi", onClose, onNewChat, onToggleHistory }) {
686
821
  children: [/* @__PURE__ */ jsx(Button, {
687
822
  variant: "ghost",
688
823
  size: "icon-sm",
689
- onClick: onNewChat,
824
+ onClick: startNewConversation,
690
825
  "aria-label": "New chat",
691
826
  children: /* @__PURE__ */ jsx(NewChatIcon, {})
692
- }), /* @__PURE__ */ jsx(Button, {
827
+ }), config.uiMode !== "full" && /* @__PURE__ */ jsx(Button, {
693
828
  variant: "ghost",
694
829
  size: "icon-sm",
695
830
  onClick: onClose,
@@ -702,7 +837,7 @@ function XHeader({ title = "Xandi", onClose, onNewChat, onToggleHistory }) {
702
837
 
703
838
  //#endregion
704
839
  //#region src/components/x-chat-history.tsx
705
- function XChatHistory({ groups = [], activeChatId, onSelectChat }) {
840
+ function XChatHistory({ items = [], isLoading = false, activeChatId, onSelectChat }) {
706
841
  return /* @__PURE__ */ jsxs("div", {
707
842
  className: "flex-1 overflow-y-auto",
708
843
  children: [/* @__PURE__ */ jsx("div", {
@@ -711,30 +846,57 @@ function XChatHistory({ groups = [], activeChatId, onSelectChat }) {
711
846
  className: "flex items-center gap-2 text-ppx-sm font-medium text-ppx-foreground",
712
847
  children: [/* @__PURE__ */ jsx(ChatIcon, {}), "Chats"]
713
848
  })
714
- }), groups.length === 0 ? /* @__PURE__ */ jsx("div", {
849
+ }), isLoading && items.length === 0 ? /* @__PURE__ */ jsxs("div", {
850
+ className: "flex flex-col items-center justify-center gap-2 px-6 py-8",
851
+ children: [/* @__PURE__ */ jsx(Spinner, {
852
+ size: "medium",
853
+ className: "text-ppx-neutral-10"
854
+ }), /* @__PURE__ */ jsx("span", {
855
+ className: "text-ppx-sm text-ppx-neutral-10",
856
+ children: "Loading conversations..."
857
+ })]
858
+ }) : items.length === 0 ? /* @__PURE__ */ jsx("div", {
715
859
  className: "px-6 py-4 text-ppx-sm text-ppx-neutral-10",
716
860
  children: "No chat history yet"
717
- }) : groups.map((group) => /* @__PURE__ */ jsxs("div", {
718
- className: "mb-2",
719
- children: [/* @__PURE__ */ jsx("div", {
720
- className: "px-6 py-2 text-ppx-xs font-medium text-ppx-neutral-10",
721
- children: group.label
722
- }), /* @__PURE__ */ jsx("div", {
723
- className: "space-y-0.5",
724
- children: group.items.map((item) => /* @__PURE__ */ jsx(Button, {
725
- variant: "ghost",
726
- onClick: () => onSelectChat?.(item.id),
727
- className: `w-full justify-start truncate rounded-none px-6 ${activeChatId === item.id ? "bg-ppx-neutral-4 text-ppx-foreground" : "text-ppx-neutral-12"}`,
728
- children: item.title
729
- }, item.id))
730
- })]
731
- }, group.label))]
861
+ }) : /* @__PURE__ */ jsx("div", {
862
+ className: "space-y-0.5",
863
+ children: items.map((item) => /* @__PURE__ */ jsx(Button, {
864
+ variant: "ghost",
865
+ onClick: () => onSelectChat?.(item.id),
866
+ className: `w-full justify-start truncate rounded-none px-6 ${activeChatId === item.id ? "bg-ppx-neutral-4 text-ppx-foreground" : "text-ppx-neutral-12"}`,
867
+ children: item.title
868
+ }, item.id))
869
+ })]
732
870
  });
733
871
  }
734
872
 
735
873
  //#endregion
736
874
  //#region src/components/x-sidebar.tsx
737
- function XSidebar({ isOpen = true, chatHistory = [], activeChatId, onClose, onNewChat, onSelectChat }) {
875
+ function XSidebar({ isOpen = true, onClose }) {
876
+ const { startNewConversation, getConvHistory, loadConversation, conversation } = useXandi();
877
+ const [chatHistoryItems, setChatHistoryItems] = useState([]);
878
+ const [isLoadingHistory, setIsLoadingHistory] = useState(false);
879
+ const fetchInProgressRef = useRef(false);
880
+ useEffect(() => {
881
+ if (!isOpen || !getConvHistory || fetchInProgressRef.current) return;
882
+ fetchInProgressRef.current = true;
883
+ setIsLoadingHistory(true);
884
+ getConvHistory().then((history) => {
885
+ setChatHistoryItems((history ?? []).map((item) => ({
886
+ id: item.id,
887
+ title: item.title,
888
+ timestamp: item.timestamp
889
+ })));
890
+ }).catch((error) => {
891
+ console.error("Failed to fetch conversation history:", error);
892
+ }).finally(() => {
893
+ fetchInProgressRef.current = false;
894
+ setIsLoadingHistory(false);
895
+ });
896
+ }, [isOpen, getConvHistory]);
897
+ const handleSelectChat = (chatId) => {
898
+ loadConversation(chatId);
899
+ };
738
900
  if (!isOpen) return null;
739
901
  return /* @__PURE__ */ jsxs("aside", {
740
902
  className: "flex h-full w-64 flex-col border-r border-ppx-neutral-5 bg-ppx-neutral-2",
@@ -753,15 +915,16 @@ function XSidebar({ isOpen = true, chatHistory = [], activeChatId, onClose, onNe
753
915
  className: "p-3",
754
916
  children: /* @__PURE__ */ jsxs(Button, {
755
917
  variant: "ghost",
756
- onClick: onNewChat,
918
+ onClick: startNewConversation,
757
919
  className: "w-full justify-start gap-3",
758
920
  children: [/* @__PURE__ */ jsx(NewChatIcon, { className: "h-5 w-5" }), "New chat"]
759
921
  })
760
922
  }),
761
923
  /* @__PURE__ */ jsx(XChatHistory, {
762
- groups: chatHistory,
763
- activeChatId,
764
- onSelectChat
924
+ items: chatHistoryItems,
925
+ isLoading: isLoadingHistory,
926
+ activeChatId: conversation.id ?? void 0,
927
+ onSelectChat: handleSelectChat
765
928
  })
766
929
  ]
767
930
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["userMessage: Message","assistantMessage: Message","value: XandiContextValue","XMessageActions.Root","XMessageActions.Feedback","XMessageActions.Copy","XMessageActions.Debug","XMessageActions.Root","XMessageActions.Feedback","XMessageActions.Copy","XMessageActions.Debug","messageType: MessageType"],"sources":["../src/context/xandi-context.tsx","../src/components/x-main-intake.tsx","../src/assets/icons/chat-icon.tsx","../src/assets/icons/check-icon.tsx","../src/assets/icons/close-icon.tsx","../src/assets/icons/copy-icon.tsx","../src/assets/icons/debug-icon.tsx","../src/assets/icons/menu-icon.tsx","../src/assets/icons/new-chat-icon.tsx","../src/assets/icons/sparkles-icon.tsx","../src/assets/icons/thumbs-down-icon.tsx","../src/assets/icons/thumbs-up-icon.tsx","../src/components/x-message-actions.tsx","../src/components/renderers/markdown-renderer.tsx","../src/components/renderers/text-renderer.tsx","../src/components/x-message-item.tsx","../src/constants.ts","../src/components/x-typing-indicator.tsx","../src/components/x-message-container.tsx","../src/components/x-welcome.tsx","../src/components/xandi.tsx","../src/components/x-header.tsx","../src/components/x-chat-history.tsx","../src/components/x-sidebar.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useState } from \"react\";\n\nexport type MessageType = \"text\" | \"markdown\";\nexport type FeedbackType = \"up\" | \"down\" | null;\n\nexport interface Message {\n id: string;\n role: \"user\" | \"assistant\";\n content: string;\n type?: MessageType;\n debugTrace?: unknown;\n}\n\nexport interface XandiResponse {\n content: string;\n type?: MessageType;\n debugTrace?: unknown;\n}\n\nexport interface XandiContextValue {\n messages: Message[];\n isLoading: boolean;\n sessionId: string | null;\n sendMessage: (text: string) => void;\n onFeedback?: (messageId: string, feedback: FeedbackType) => void;\n}\n\nconst XandiContext = createContext<XandiContextValue | null>(null);\n\nexport interface XandiProviderProps {\n fetchResponse: (message: string) => Promise<XandiResponse>;\n sessionId?: string;\n onFeedback?: (messageId: string, feedback: FeedbackType) => void;\n children: React.ReactNode;\n}\n\nexport function XandiProvider({\n fetchResponse,\n sessionId: initialSessionId,\n onFeedback,\n children,\n}: XandiProviderProps) {\n const [sessionId, setSessionId] = useState<string | null>(initialSessionId ?? null);\n const [messages, setMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n\n // Initialize sessionId if not provided\n useEffect(() => {\n if (!initialSessionId) {\n setSessionId(crypto.randomUUID());\n }\n }, [initialSessionId]);\n\n const sendMessage = async (text: string) => {\n if (!text.trim() || isLoading) return;\n\n // Add user message\n const userMessage: Message = {\n id: crypto.randomUUID(),\n role: \"user\",\n content: text,\n };\n setMessages((prev) => [...prev, userMessage]);\n setIsLoading(true);\n\n try {\n const response = await fetchResponse(text);\n\n const assistantMessage: Message = {\n id: crypto.randomUUID(),\n role: \"assistant\",\n content: response.content,\n type: response.type,\n debugTrace: response.debugTrace,\n };\n setMessages((prev) => [...prev, assistantMessage]);\n } catch (error) {\n console.error(\"Failed to send message:\", error);\n } finally {\n setIsLoading(false);\n }\n };\n\n const value: XandiContextValue = {\n messages,\n isLoading,\n sessionId,\n sendMessage,\n onFeedback,\n };\n\n return <XandiContext.Provider value={value}>{children}</XandiContext.Provider>;\n}\n\nexport function useXandi(): XandiContextValue {\n const context = useContext(XandiContext);\n if (!context) {\n throw new Error(\"useXandi must be used within XandiProvider\");\n }\n return context;\n}\n","import { useState } from \"react\";\n\nimport { Button, FileIcon, SendIcon, StopIcon } from \"@px-ui/core\";\nimport { useXandi } from \"../context/xandi-context\";\n\nexport interface Suggestion {\n id: string;\n label: string;\n prompt: string;\n}\n\nexport interface XMainIntakeProps {\n placeholder?: string;\n suggestions?: Suggestion[];\n}\n\nexport function XMainIntake({ \n placeholder = \"Ask about jobs, candidates, timesheets, or anything workforce...\",\n suggestions = [],\n}: XMainIntakeProps) {\n const { isLoading, sendMessage } = useXandi();\n const [input, setInput] = useState(\"\");\n\n const handleSubmit = (e?: React.FormEvent) => {\n e?.preventDefault();\n if (input.trim() && !isLoading) {\n sendMessage(input);\n setInput(\"\");\n }\n };\n\n const handleSuggestionClick = (prompt: string) => {\n setInput(prompt);\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <form onSubmit={handleSubmit} className=\"flex flex-col gap-2 rounded-2xl border border-ppx-neutral-5 bg-ppx-neutral-1 p-3\">\n <div className=\"uploads-section\"></div>\n\n <XIntakeTextarea\n value={input}\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder={placeholder}\n disabled={isLoading}\n />\n\n <div className=\"actions-section flex flex-row items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n disabled={isLoading}\n >\n <FileIcon width={20} />\n </Button>\n <span className=\"ml-auto text-ppx-xs text-ppx-neutral-10\">\n Enter to send · Shift+Enter for new line\n </span>\n <Button\n type=\"submit\"\n size=\"icon-sm\"\n disabled={isLoading || !input.trim()}\n className=\"flex h-8 w-8 items-center justify-center rounded-full bg-ppx-green-5 text-white transition-all hover:bg-ppx-green-4 hover:shadow-[0_0_12px_rgba(40,182,116,0.6)] disabled:bg-ppx-neutral-5 disabled:text-ppx-neutral-10 disabled:shadow-none\"\n >\n {isLoading ? (\n <StopIcon width={14} />\n ) : (\n <SendIcon width={16} />\n )}\n </Button>\n </div>\n </form>\n\n {suggestions.length > 0 && (\n <div className=\"flex flex-wrap justify-center gap-2\">\n {suggestions.map((suggestion, index) => (\n <Button\n key={suggestion.id}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleSuggestionClick(suggestion.prompt)}\n className=\"animate-[popUp_0.3s_ease-out_forwards] rounded-full opacity-0\"\n style={{ animationDelay: `${index * 0.1}s` }}\n >\n {suggestion.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n\n/////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////Supporting Components//////////////////////////////////////\n/////////////////////////////////////////////////////////////////////////////////////////////////\n\ninterface XIntakeTextareaProps {\n value: string;\n onChange: (value: string) => void;\n onSubmit: () => void;\n placeholder?: string;\n disabled?: boolean;\n}\n\nfunction XIntakeTextarea({ value, onChange, onSubmit, placeholder, disabled }: XIntakeTextareaProps) {\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n onSubmit();\n }\n // Shift+Enter allows default behavior (newline)\n };\n\n return (\n <textarea\n className=\"w-full resize-none border-none bg-transparent outline-none\"\n placeholder={placeholder}\n disabled={disabled}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onKeyDown={handleKeyDown}\n />\n );\n}\n","export function ChatIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\n","export function CheckIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\n","export function CloseIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\n","export function CopyIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n );\n}\n\n","export function DebugIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1\" />\n <path d=\"M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1\" />\n </svg>\n );\n}\n\n","export function MenuIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\n <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" />\n <line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\n","export function NewChatIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" />\n <path d=\"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z\" />\n </svg>\n );\n}\n\n","export function SparklesIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\" />\n <path d=\"M20 3v4\" />\n <path d=\"M22 5h-4\" />\n <path d=\"M4 17v2\" />\n <path d=\"M5 18H3\" />\n </svg>\n );\n}\n\n","export function ThumbsDownIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M17 14V2\" />\n <path d=\"M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z\" />\n </svg>\n );\n}\n\n","export function ThumbsUpIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M7 10v12\" />\n <path d=\"M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z\" />\n </svg>\n );\n}\n\n","import { useState } from \"react\";\nimport { Button, Dialog, toast, Tooltip } from \"@px-ui/core\";\n\nimport {\n CheckIcon,\n CopyIcon,\n DebugIcon,\n ThumbsDownIcon,\n ThumbsUpIcon,\n} from \"../assets/icons\";\nimport { useXandi, type FeedbackType } from \"../context/xandi-context\";\n\nexport type { FeedbackType } from \"../context/xandi-context\";\n\n// ============================================================================\n// Root\n// ============================================================================\n\n/**\n * Container for message actions. Use with composable children:\n * - XMessageActions.Feedback\n * - XMessageActions.Copy\n * - XMessageActions.Debug\n */\nexport function Root({ children }: { children: React.ReactNode }) {\n return <div className=\"flex items-center gap-1\">{children}</div>;\n}\n\n// ============================================================================\n// Feedback\n// ============================================================================\n\nexport interface FeedbackProps {\n messageId: string;\n}\n\nexport function Feedback({ messageId }: FeedbackProps) {\n const { onFeedback } = useXandi();\n const [feedback, setFeedback] = useState<FeedbackType>(null);\n\n const handleFeedback = (type: FeedbackType) => {\n const newFeedback = feedback === type ? null : type;\n setFeedback(newFeedback);\n onFeedback?.(messageId, newFeedback);\n };\n\n return (\n <>\n {/* Thumbs Up */}\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => handleFeedback(\"up\")}\n className={`h-7 w-7 ${\n feedback === \"up\"\n ? \"bg-ppx-green-2 text-ppx-green-5\"\n : \"text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n }`}\n >\n <ThumbsUpIcon className={feedback === \"up\" ? \"fill-current\" : \"\"} />\n </Button>\n }\n />\n <Tooltip.Content>\n {feedback === \"up\" ? \"You found this helpful\" : \"Good response\"}\n </Tooltip.Content>\n </Tooltip.Root>\n\n {/* Thumbs Down */}\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => handleFeedback(\"down\")}\n className={`h-7 w-7 ${\n feedback === \"down\"\n ? \"bg-ppx-red-2 text-ppx-red-5\"\n : \"text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n }`}\n >\n <ThumbsDownIcon className={feedback === \"down\" ? \"fill-current\" : \"\"} />\n </Button>\n }\n />\n <Tooltip.Content>\n {feedback === \"down\" ? \"You found this unhelpful\" : \"Bad response\"}\n </Tooltip.Content>\n </Tooltip.Root>\n </>\n );\n}\n\n// ============================================================================\n// Copy\n// ============================================================================\n\nexport interface CopyProps {\n content: string;\n}\n\nexport function Copy({ content }: CopyProps) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(content);\n setCopied(true);\n toast.add({\n title: \"Copied!\",\n description: \"Message copied to clipboard\",\n type: \"success\",\n });\n setTimeout(() => setCopied(false), 2000);\n } catch {\n toast.add({\n title: \"Failed to copy\",\n description: \"Could not copy message to clipboard\",\n type: \"error\",\n });\n }\n };\n\n return (\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={handleCopy}\n className=\"h-7 w-7 text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n >\n {copied ? (\n <CheckIcon className=\"text-ppx-green-5\" />\n ) : (\n <CopyIcon />\n )}\n </Button>\n }\n />\n <Tooltip.Content>\n {copied ? \"Copied!\" : \"Copy message\"}\n </Tooltip.Content>\n </Tooltip.Root>\n );\n}\n\n// ============================================================================\n// Debug\n// ============================================================================\n\nexport interface DebugProps {\n messageId: string;\n debugTrace: unknown;\n}\n\nexport function Debug({ messageId, debugTrace }: DebugProps) {\n const [debugOpen, setDebugOpen] = useState(false);\n\n return (\n <Dialog.Root open={debugOpen} onOpenChange={setDebugOpen}>\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Dialog.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"h-7 w-7 text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n >\n <DebugIcon />\n </Button>\n }\n />\n }\n />\n <Tooltip.Content>\n View debug trace\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Dialog.Portal>\n <Dialog.Overlay />\n <Dialog.Content className=\"max-w-2xl\">\n <Dialog.Header>\n <Dialog.Title>Debug Trace</Dialog.Title>\n <Dialog.Description>\n Response debug information for message {messageId}\n </Dialog.Description>\n </Dialog.Header>\n <div className=\"max-h-96 overflow-auto rounded bg-ppx-neutral-2 p-4\">\n <pre className=\"whitespace-pre-wrap font-mono text-ppx-xs text-ppx-neutral-12\">\n {JSON.stringify(debugTrace, null, 2)}\n </pre>\n </div>\n <Dialog.Footer>\n <Dialog.Close render={<Button variant=\"outline\">Close</Button>} />\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","import ReactMarkdown from \"react-markdown\";\n\nimport type { Message } from \"../../context/xandi-context\";\nimport * as XMessageActions from \"../x-message-actions\";\n\nexport interface MarkdownRendererProps {\n message: Message;\n}\n\nexport function MarkdownRenderer({ message }: MarkdownRendererProps) {\n const isUser = message.role === \"user\";\n const baseClass = `text-ppx-sm leading-relaxed ${isUser ? \"text-white\" : \"text-ppx-neutral-13\"}`;\n const showActions = !isUser;\n\n return (\n <div>\n <div className={baseClass}>\n <ReactMarkdown\n components={{\n p: ({ children }) => <p className=\"mb-2 last:mb-0\">{children}</p>,\n ul: ({ children }) => <ul className=\"mb-2 list-disc pl-4\">{children}</ul>,\n ol: ({ children }) => <ol className=\"mb-2 list-decimal pl-4\">{children}</ol>,\n li: ({ children }) => <li className=\"mb-1\">{children}</li>,\n a: ({ href, children }) => (\n <a href={href} className=\"underline\" target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n ),\n strong: ({ children }) => <strong className=\"font-semibold\">{children}</strong>,\n }}\n >\n {message.content}\n </ReactMarkdown>\n </div>\n {showActions && (\n <div className=\"mt-2\">\n <XMessageActions.Root>\n <XMessageActions.Feedback messageId={message.id} />\n <XMessageActions.Copy content={message.content} />\n {message.debugTrace != null && (\n <XMessageActions.Debug messageId={message.id} debugTrace={message.debugTrace} />\n )}\n </XMessageActions.Root>\n </div>\n )}\n </div>\n );\n}\n","import type { Message } from \"../../context/xandi-context\";\nimport * as XMessageActions from \"../x-message-actions\";\n\nexport interface TextRendererProps {\n message: Message;\n}\n\nexport function TextRenderer({ message }: TextRendererProps) {\n const isUser = message.role === \"user\";\n const showActions = !isUser;\n\n return (\n <div>\n <p className={`text-ppx-sm leading-relaxed ${isUser ? \"text-white\" : \"text-ppx-neutral-13\"}`}>\n {message.content}\n </p>\n {showActions && (\n <div className=\"mt-2\">\n <XMessageActions.Root>\n <XMessageActions.Feedback messageId={message.id} />\n <XMessageActions.Copy content={message.content} />\n {message.debugTrace != null && (\n <XMessageActions.Debug messageId={message.id} debugTrace={message.debugTrace} />\n )}\n </XMessageActions.Root>\n </div>\n )}\n </div>\n );\n}\n","import type { Message, MessageType } from \"../context/xandi-context\";\nimport { MarkdownRenderer } from \"./renderers/markdown-renderer\";\nimport { TextRenderer } from \"./renderers/text-renderer\";\n\nexport interface XMessageItemProps {\n message: Message;\n}\n\n/**\n * Router component that renders messages based on their type.\n * Defaults to markdown rendering if no type is specified.\n */\nexport function XMessageItem({ message }: XMessageItemProps) {\n const isUser = message.role === \"user\";\n const messageType: MessageType = message.type ?? \"markdown\";\n\n return (\n <div className={`flex ${isUser ? \"justify-end\" : \"justify-start\"}`}>\n {isUser ? (\n <div className=\"max-w-[90%] rounded-2xl rounded-br-sm bg-ppx-green-5 px-4 py-2.5\">\n <MessageRenderer type={messageType} message={message} />\n </div>\n ) : (\n <div className=\"max-w-[90%]\">\n <MessageRenderer type={messageType} message={message} />\n </div>\n )}\n </div>\n );\n}\n\ninterface MessageRendererProps {\n type: MessageType;\n message: Message;\n}\n\n/**\n * Switch component that selects the appropriate renderer based on message type.\n */\nfunction MessageRenderer({ type, message }: MessageRendererProps) {\n switch (type) {\n case \"text\":\n return <TextRenderer message={message} />;\n case \"markdown\":\n default:\n return <MarkdownRenderer message={message} />;\n }\n}\n","// Xandi avatar asset path\n// Wrapped in try-catch for SSR compatibility (import.meta.url may not work in all environments)\nfunction getXandiAvatarUrl(): string {\n try {\n return new URL(\"./assets/images/xandi-avatar.png\", import.meta.url).href;\n } catch {\n // Fallback for SSR environments where import.meta.url is not a valid URL\n return \"\";\n }\n}\n\nexport const XANDI_AVATAR_URL = getXandiAvatarUrl();\n","import { Avatar } from \"@px-ui/core\";\n\nimport { XANDI_AVATAR_URL } from \"../constants\";\n\nexport function XTypingIndicator() {\n return (\n <div className=\"flex items-center gap-4\">\n <div className=\"animate-[popUp_0.3s_ease-out_forwards]\">\n <Avatar\n imgSrc={XANDI_AVATAR_URL}\n name=\"Xandi\"\n variant=\"rounded\"\n size=\"48px\"\n hideTooltip\n className=\"border-2 border-ppx-neutral-4\"\n />\n </div>\n <div className=\"flex animate-[slideIn_0.3s_ease-out_0.2s_forwards] items-center gap-2 rounded-xl bg-ppx-neutral-2 px-[10px] pb-[5px] pt-[10px] opacity-0 shadow-sm\">\n <span className=\"h-[12px] w-[12px] animate-bounce rounded-full bg-ppx-neutral-9 [animation-delay:-0.3s]\" />\n <span className=\"h-[12px] w-[12px] animate-bounce rounded-full bg-ppx-neutral-6 [animation-delay:-0.15s]\" />\n <span className=\"h-[12px] w-[12px] animate-bounce rounded-full bg-ppx-neutral-9\" />\n </div>\n </div>\n );\n}\n","import { useEffect, useRef } from \"react\";\n\nimport { XMessageItem } from \"./x-message-item\";\nimport { XTypingIndicator } from \"./x-typing-indicator\";\nimport { useXandi } from \"../context/xandi-context\";\n\nexport interface XMessageContainerProps {\n height?: string | number;\n}\n\nexport function XMessageContainer({ height = 400 }: XMessageContainerProps) {\n const { messages, isLoading } = useXandi();\n const containerRef = useRef<HTMLDivElement>(null);\n\n // Auto-scroll to bottom when new messages arrive or loading state changes\n useEffect(() => {\n if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n }, [messages, isLoading]);\n\n return (\n <div\n ref={containerRef}\n className=\"overflow-y-auto py-[10px]\"\n style={{ height: typeof height === \"number\" ? `${height}px` : height }}\n >\n <div className=\"flex flex-col gap-5 p-4\">\n {messages.map((message) => (\n <XMessageItem key={message.id} message={message} />\n ))}\n {isLoading && <XTypingIndicator />}\n </div>\n </div>\n );\n}\n","import { Avatar } from \"@px-ui/core\";\n\nimport { SparklesIcon } from \"../assets/icons\";\nimport { XANDI_AVATAR_URL } from \"../constants\";\n\nexport interface XWelcomeProps {\n message: string;\n}\n\nexport function XWelcome({ message }: XWelcomeProps) {\n return (\n <div className=\"flex flex-col items-center justify-center gap-4 py-12\">\n <div className=\"relative\">\n {/* Gradient border ring */}\n <div className=\"absolute -inset-1 rounded-full bg-gradient-to-b from-ppx-green-4 via-ppx-green-5/50 to-transparent\" />\n \n {/* Avatar container */}\n <div className=\"relative rounded-full bg-ppx-neutral-18 p-1\">\n <Avatar\n imgSrc={XANDI_AVATAR_URL}\n name=\"Xandi\"\n variant=\"rounded\"\n size=\"120px\"\n hideTooltip\n />\n </div>\n\n {/* Sparkles icon with pulse-zoom animation */}\n <div className=\"absolute -bottom-2 left-1/2\">\n <div className=\"flex h-6 w-6 animate-[pulse-zoom_2s_ease-in-out_infinite] items-center justify-center rounded-full bg-ppx-green-5\">\n <SparklesIcon className=\"text-white\" />\n </div>\n </div>\n </div>\n\n <h2 className=\"text-ppx-xl font-semibold text-ppx-foreground\">\n {message}\n </h2>\n </div>\n );\n}\n","import { XMainIntake, type Suggestion } from \"./x-main-intake\";\nimport { XMessageContainer } from \"./x-message-container\";\nimport { XWelcome } from \"./x-welcome\";\nimport { useXandi } from \"../context/xandi-context\";\n\nexport interface XandiProps {\n welcomeMessage?: string;\n suggestions?: Suggestion[];\n}\n\nexport function Xandi({\n welcomeMessage = \"How can I help you today?\",\n suggestions = [],\n}: XandiProps) {\n const { messages, isLoading } = useXandi();\n const isEmpty = messages.length === 0;\n\n return (\n <div className=\"flex flex-col\">\n {isEmpty ? (\n <XWelcome message={welcomeMessage} />\n ) : (\n <XMessageContainer />\n )}\n <XMainIntake suggestions={isEmpty ? suggestions : []} />\n </div>\n );\n}\n","import { Avatar, Button } from \"@px-ui/core\";\n\nimport { CloseIcon, MenuIcon, NewChatIcon } from \"../assets/icons\";\nimport { XANDI_AVATAR_URL } from \"../constants\";\n\nexport interface XHeaderProps {\n title?: string;\n onClose?: () => void;\n onNewChat?: () => void;\n onToggleHistory?: () => void;\n}\n\nexport function XHeader({\n title = \"Xandi\",\n onClose,\n onNewChat,\n onToggleHistory,\n}: XHeaderProps) {\n return (\n <header className=\"flex items-center justify-between border-b border-ppx-neutral-5 bg-ppx-neutral-2 px-3 py-2\">\n {/* Left section - Menu & Title */}\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onToggleHistory}\n aria-label=\"Toggle chat history\"\n >\n <MenuIcon />\n </Button>\n\n <div className=\"flex items-center gap-2\">\n <Avatar\n imgSrc={XANDI_AVATAR_URL}\n name=\"Xandi\"\n variant=\"rounded\"\n size=\"24px\"\n hideTooltip\n />\n <span className=\"font-medium text-ppx-foreground\">{title}</span>\n </div>\n </div>\n\n {/* Right section - Actions */}\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onNewChat}\n aria-label=\"New chat\"\n >\n <NewChatIcon />\n </Button>\n\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <CloseIcon />\n </Button>\n </div>\n </header>\n );\n}\n","import { Button } from \"@px-ui/core\";\n\nimport { ChatIcon } from \"../assets/icons\";\n\nexport interface ChatHistoryItem {\n id: string;\n title: string;\n timestamp: Date;\n}\n\nexport interface ChatHistoryGroup {\n label: string;\n items: ChatHistoryItem[];\n}\n\nexport interface XChatHistoryProps {\n groups?: ChatHistoryGroup[];\n activeChatId?: string;\n onSelectChat?: (chatId: string) => void;\n}\n\nexport function XChatHistory({\n groups = [],\n activeChatId,\n onSelectChat,\n}: XChatHistoryProps) {\n return (\n <div className=\"flex-1 overflow-y-auto\">\n {/* Header */}\n <div className=\"px-3 py-2\">\n <div className=\"flex items-center gap-2 text-ppx-sm font-medium text-ppx-foreground\">\n <ChatIcon />\n Chats\n </div>\n </div>\n\n {/* Chat List */}\n {groups.length === 0 ? (\n <div className=\"px-6 py-4 text-ppx-sm text-ppx-neutral-10\">\n No chat history yet\n </div>\n ) : (\n groups.map((group) => (\n <div key={group.label} className=\"mb-2\">\n <div className=\"px-6 py-2 text-ppx-xs font-medium text-ppx-neutral-10\">\n {group.label}\n </div>\n <div className=\"space-y-0.5\">\n {group.items.map((item) => (\n <Button\n key={item.id}\n variant=\"ghost\"\n onClick={() => onSelectChat?.(item.id)}\n className={`w-full justify-start truncate rounded-none px-6 ${\n activeChatId === item.id\n ? \"bg-ppx-neutral-4 text-ppx-foreground\"\n : \"text-ppx-neutral-12\"\n }`}\n >\n {item.title}\n </Button>\n ))}\n </div>\n </div>\n ))\n )}\n </div>\n );\n}\n\n","import { Button } from \"@px-ui/core\";\n\nimport { CloseIcon, NewChatIcon } from \"../assets/icons\";\nimport { XChatHistory, type ChatHistoryGroup } from \"./x-chat-history\";\n\nexport interface XSidebarProps {\n isOpen?: boolean;\n chatHistory?: ChatHistoryGroup[];\n activeChatId?: string;\n onClose?: () => void;\n onNewChat?: () => void;\n onSelectChat?: (chatId: string) => void;\n}\n\nexport function XSidebar({\n isOpen = true,\n chatHistory = [],\n activeChatId,\n onClose,\n onNewChat,\n onSelectChat,\n}: XSidebarProps) {\n if (!isOpen) return null;\n\n return (\n <aside className=\"flex h-full w-64 flex-col border-r border-ppx-neutral-5 bg-ppx-neutral-2\">\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-ppx-neutral-5 p-3\">\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClose}\n aria-label=\"Close sidebar\"\n >\n <CloseIcon />\n </Button>\n </div>\n\n {/* New Chat Button */}\n <div className=\"p-3\">\n <Button\n variant=\"ghost\"\n onClick={onNewChat}\n className=\"w-full justify-start gap-3\"\n >\n <NewChatIcon className=\"h-5 w-5\" />\n New chat\n </Button>\n </div>\n\n {/* Chat History */}\n <XChatHistory\n groups={chatHistory}\n activeChatId={activeChatId}\n onSelectChat={onSelectChat}\n />\n </aside>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["defaultConfig: Required<XandiConfig>","defaultGetConvOptions: Required<GetConvOptions>","config: Required<XandiConfig>","userMessage: Message","assistantMessage: Message","value: XandiContextValue","XMessageActions.Root","XMessageActions.Feedback","XMessageActions.Copy","XMessageActions.Debug","XMessageActions.Root","XMessageActions.Feedback","XMessageActions.Copy","XMessageActions.Debug","messageType: MessageType"],"sources":["../src/constants.ts","../src/context/xandi-context.tsx","../src/components/x-main-intake.tsx","../src/assets/icons/chat-icon.tsx","../src/assets/icons/check-icon.tsx","../src/assets/icons/close-icon.tsx","../src/assets/icons/copy-icon.tsx","../src/assets/icons/debug-icon.tsx","../src/assets/icons/menu-icon.tsx","../src/assets/icons/new-chat-icon.tsx","../src/assets/icons/sparkles-icon.tsx","../src/assets/icons/thumbs-down-icon.tsx","../src/assets/icons/thumbs-up-icon.tsx","../src/components/x-message-actions.tsx","../src/components/renderers/markdown-renderer.tsx","../src/components/renderers/text-renderer.tsx","../src/components/x-message-item.tsx","../src/components/x-typing-indicator.tsx","../src/components/x-message-container.tsx","../src/components/x-welcome.tsx","../src/components/xandi.tsx","../src/components/x-header.tsx","../src/components/x-chat-history.tsx","../src/components/x-sidebar.tsx"],"sourcesContent":["function getXandiAvatarUrl(): string {\n try {\n return new URL(\"./assets/images/xandi-avatar.png\", import.meta.url).href;\n } catch {\n return \"\";\n }\n}\n\nexport const XANDI_AVATAR_URL = getXandiAvatarUrl();\n","import { createContext, useContext, useEffect, useRef, useState } from \"react\";\n\nimport { XANDI_AVATAR_URL } from \"../constants\";\n\nexport type MessageType = \"text\" | \"markdown\";\nexport type FeedbackType = \"up\" | \"down\" | null;\n\nexport interface Message {\n id: string;\n role: \"user\" | \"assistant\";\n content: string;\n type?: MessageType;\n debugTrace?: unknown;\n}\n\nexport interface XandiApiResponse {\n success: boolean;\n message: string;\n data: {\n intent: string;\n data: unknown;\n conversation_id: string;\n };\n trace?: {\n trace_id: string;\n execution_mode: string;\n intent: string;\n tool_id: string;\n debug_trace: unknown;\n };\n}\n\nexport interface XandiResponse {\n content: string;\n type?: MessageType;\n debugTrace?: unknown;\n conversationId?: string;\n}\n\nexport interface ConversationSummary {\n id: string;\n title: string;\n timestamp: Date;\n}\n\nexport interface Conversation {\n id: string | null;\n title: string;\n messages: Message[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nfunction createEmptyConversation(): Conversation {\n return {\n id: null,\n title: \"New Chat\",\n messages: [],\n createdAt: new Date(),\n updatedAt: new Date(),\n };\n}\n\nexport type XandiUIMode = \"full\" | \"sidebar\" | \"floating\";\n\nexport interface XandiConfig {\n avatarUrl?: string;\n assistantName?: string;\n uiMode?: XandiUIMode;\n}\n\nconst defaultConfig: Required<XandiConfig> = {\n avatarUrl: XANDI_AVATAR_URL,\n assistantName: \"Xandi\",\n uiMode: \"full\",\n};\n\nexport interface FetchRespOptions {\n conversationId?: string;\n signal?: AbortSignal;\n}\n\nexport interface GetConvOptions {\n page?: number;\n perPage?: number;\n}\n\nconst defaultGetConvOptions: Required<GetConvOptions> = {\n page: 1,\n perPage: 20,\n};\n\nexport interface XandiHandlers {\n fetchResp: (message: string, options?: FetchRespOptions) => Promise<XandiResponse>;\n getConv?: (conversationId: string, options?: GetConvOptions) => Promise<Conversation>;\n getConvHistory?: () => Promise<ConversationSummary[]>;\n onFeedback?: (messageId: string, conversationId: string, feedback: FeedbackType) => void;\n onStop?: (conversationId: string) => void;\n}\n\nexport interface XandiContextValue {\n conversation: Conversation;\n isLoading: boolean;\n sendMessage: (text: string) => void;\n stopRequest: () => void;\n loadConversation: (conversationId: string, options?: GetConvOptions) => Promise<void>;\n startNewConversation: () => void;\n submitFeedback: (messageId: string, feedback: FeedbackType) => void;\n getConvHistory?: () => Promise<ConversationSummary[]>;\n config: Required<XandiConfig>;\n setUiModeOverride: (mode: XandiUIMode | null) => void;\n}\n\nconst XandiContext = createContext<XandiContextValue | null>(null);\n\nexport interface XandiProviderProps {\n handlers: XandiHandlers;\n conversationId?: string;\n config?: XandiConfig;\n children: React.ReactNode;\n}\n\nexport function XandiProvider({\n handlers,\n conversationId: initialConversationId,\n config: userConfig,\n children,\n}: XandiProviderProps) {\n const [conversation, setConversation] = useState<Conversation>(createEmptyConversation);\n const [isLoading, setIsLoading] = useState(false);\n const [uiModeOverride, setUiModeOverride] = useState<XandiUIMode | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const config: Required<XandiConfig> = {\n ...defaultConfig,\n ...userConfig,\n uiMode: uiModeOverride ?? userConfig?.uiMode ?? defaultConfig.uiMode,\n };\n\n useEffect(() => {\n if (initialConversationId && handlers.getConv) {\n loadConversation(initialConversationId);\n }\n }, [initialConversationId]);\n\n const loadConversation = async (convId: string, options?: GetConvOptions) => {\n if (!handlers.getConv) return;\n\n const opts = { ...defaultGetConvOptions, ...options };\n\n try {\n setIsLoading(true);\n const loadedConversation = await handlers.getConv(convId, opts);\n setConversation(loadedConversation);\n } catch (error) {\n console.error(\"Failed to load conversation:\", error);\n } finally {\n setIsLoading(false);\n }\n };\n\n const startNewConversation = () => {\n setConversation(createEmptyConversation());\n };\n\n const sendMessage = async (text: string) => {\n if (!text.trim() || isLoading) return;\n\n const userMessage: Message = {\n id: crypto.randomUUID(),\n role: \"user\",\n content: text,\n };\n setConversation((prev) => ({\n ...prev,\n messages: [...prev.messages, userMessage],\n updatedAt: new Date(),\n }));\n setIsLoading(true);\n\n abortControllerRef.current = new AbortController();\n\n try {\n const response = await handlers.fetchResp(text, {\n conversationId: conversation.id ?? undefined,\n signal: abortControllerRef.current.signal,\n });\n\n const assistantMessage: Message = {\n id: crypto.randomUUID(),\n role: \"assistant\",\n content: response.content,\n type: response.type,\n debugTrace: response.debugTrace,\n };\n\n setConversation((prev) => ({\n ...prev,\n id: response.conversationId ?? prev.id,\n messages: [...prev.messages, assistantMessage],\n updatedAt: new Date(),\n }));\n } catch (error) {\n if ((error as Error).name !== \"AbortError\") {\n console.error(\"Failed to send message:\", error);\n }\n } finally {\n setIsLoading(false);\n abortControllerRef.current = null;\n }\n };\n\n const stopRequest = () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n }\n if (conversation.id && handlers.onStop) {\n handlers.onStop(conversation.id);\n }\n setIsLoading(false);\n };\n\n const submitFeedback = (messageId: string, feedback: FeedbackType) => {\n if (handlers.onFeedback && conversation.id) {\n handlers.onFeedback(messageId, conversation.id, feedback);\n }\n };\n\n const value: XandiContextValue = {\n conversation,\n isLoading,\n sendMessage,\n stopRequest,\n loadConversation,\n startNewConversation,\n submitFeedback,\n getConvHistory: handlers.getConvHistory,\n config,\n setUiModeOverride,\n };\n\n return <XandiContext.Provider value={value}>{children}</XandiContext.Provider>;\n}\n\nexport function useXandi(): XandiContextValue {\n const context = useContext(XandiContext);\n if (!context) {\n throw new Error(\"useXandi must be used within XandiProvider\");\n }\n return context;\n}\n","import { useState } from \"react\";\n\nimport { Button, FileIcon, SendIcon, StopIcon } from \"@px-ui/core\";\nimport { useXandi } from \"../context/xandi-context\";\n\nexport interface Suggestion {\n id: string;\n label: string;\n prompt: string;\n}\n\nexport interface XMainIntakeProps {\n placeholder?: string;\n suggestions?: Suggestion[];\n}\n\nexport function XMainIntake({ \n placeholder = \"Ask about jobs, candidates, timesheets, or anything workforce...\",\n suggestions = [],\n}: XMainIntakeProps) {\n const { isLoading, sendMessage, stopRequest } = useXandi();\n const [input, setInput] = useState(\"\");\n\n const handleSubmit = (e?: React.FormEvent) => {\n e?.preventDefault();\n if (input.trim() && !isLoading) {\n sendMessage(input);\n setInput(\"\");\n }\n };\n\n const handleSuggestionClick = (prompt: string) => {\n setInput(prompt);\n };\n\n const handleStopOrSend = () => {\n if (isLoading) {\n stopRequest();\n }\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <form onSubmit={handleSubmit} className=\"flex flex-col gap-2 rounded-2xl border border-ppx-neutral-5 bg-ppx-neutral-1 p-3\">\n <div className=\"uploads-section\"></div>\n\n <XIntakeTextarea\n value={input}\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder={placeholder}\n disabled={isLoading}\n />\n\n <div className=\"actions-section flex flex-row items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n disabled={isLoading}\n >\n <FileIcon width={20} />\n </Button>\n <span className=\"ml-auto text-ppx-xs text-ppx-neutral-10\">\n Enter to send · Shift+Enter for new line\n </span>\n {isLoading ? (\n <Button\n type=\"button\"\n size=\"icon-sm\"\n onClick={handleStopOrSend}\n className=\"flex h-8 w-8 items-center justify-center rounded-full bg-ppx-red-5 text-white transition-all hover:bg-ppx-red-4 hover:shadow-[0_0_12px_rgba(220,38,38,0.6)]\"\n >\n <StopIcon width={14} />\n </Button>\n ) : (\n <Button\n type=\"submit\"\n size=\"icon-sm\"\n disabled={!input.trim()}\n className=\"flex h-8 w-8 items-center justify-center rounded-full bg-ppx-green-5 text-white transition-all hover:bg-ppx-green-4 hover:shadow-[0_0_12px_rgba(40,182,116,0.6)] disabled:bg-ppx-neutral-5 disabled:text-ppx-neutral-10 disabled:shadow-none\"\n >\n <SendIcon width={16} />\n </Button>\n )}\n </div>\n </form>\n\n {suggestions.length > 0 && (\n <div className=\"flex flex-wrap justify-center gap-2\">\n {suggestions.map((suggestion, index) => (\n <Button\n key={suggestion.id}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleSuggestionClick(suggestion.prompt)}\n className=\"animate-[popUp_0.3s_ease-out_forwards] rounded-full opacity-0\"\n style={{ animationDelay: `${index * 0.1}s` }}\n >\n {suggestion.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\ninterface XIntakeTextareaProps {\n value: string;\n onChange: (value: string) => void;\n onSubmit: () => void;\n placeholder?: string;\n disabled?: boolean;\n}\n\nfunction XIntakeTextarea({ value, onChange, onSubmit, placeholder, disabled }: XIntakeTextareaProps) {\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n onSubmit();\n }\n };\n\n return (\n <textarea\n className=\"w-full resize-none border-none bg-transparent outline-none\"\n placeholder={placeholder}\n disabled={disabled}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onKeyDown={handleKeyDown}\n />\n );\n}\n","export function ChatIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\n","export function CheckIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\n","export function CloseIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\n","export function CopyIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n );\n}\n\n","export function DebugIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1\" />\n <path d=\"M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1\" />\n </svg>\n );\n}\n\n","export function MenuIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\n <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" />\n <line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n </svg>\n );\n}\n\n","export function NewChatIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" />\n <path d=\"M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z\" />\n </svg>\n );\n}\n\n","export function SparklesIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\" />\n <path d=\"M20 3v4\" />\n <path d=\"M22 5h-4\" />\n <path d=\"M4 17v2\" />\n <path d=\"M5 18H3\" />\n </svg>\n );\n}\n\n","export function ThumbsDownIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M17 14V2\" />\n <path d=\"M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z\" />\n </svg>\n );\n}\n\n","export function ThumbsUpIcon(props: React.ComponentProps<\"svg\">) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M7 10v12\" />\n <path d=\"M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z\" />\n </svg>\n );\n}\n\n","import { useState } from \"react\";\nimport { Button, Dialog, toast, Tooltip } from \"@px-ui/core\";\n\nimport {\n CheckIcon,\n CopyIcon,\n DebugIcon,\n ThumbsDownIcon,\n ThumbsUpIcon,\n} from \"../assets/icons\";\nimport { useXandi, type FeedbackType } from \"../context/xandi-context\";\n\nexport type { FeedbackType } from \"../context/xandi-context\";\n\nexport function Root({ children }: { children: React.ReactNode }) {\n return <div className=\"flex items-center gap-1\">{children}</div>;\n}\n\nexport interface FeedbackProps {\n messageId: string;\n}\n\nexport function Feedback({ messageId }: FeedbackProps) {\n const { submitFeedback } = useXandi();\n const [feedback, setFeedback] = useState<FeedbackType>(null);\n\n const handleFeedback = (type: FeedbackType) => {\n const newFeedback = feedback === type ? null : type;\n setFeedback(newFeedback);\n submitFeedback(messageId, newFeedback);\n };\n\n return (\n <>\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => handleFeedback(\"up\")}\n className={`h-7 w-7 ${\n feedback === \"up\"\n ? \"bg-ppx-green-2 text-ppx-green-5\"\n : \"text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n }`}\n >\n <ThumbsUpIcon className={feedback === \"up\" ? \"fill-current\" : \"\"} />\n </Button>\n }\n />\n <Tooltip.Content>\n {feedback === \"up\" ? \"You found this helpful\" : \"Good response\"}\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => handleFeedback(\"down\")}\n className={`h-7 w-7 ${\n feedback === \"down\"\n ? \"bg-ppx-red-2 text-ppx-red-5\"\n : \"text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n }`}\n >\n <ThumbsDownIcon className={feedback === \"down\" ? \"fill-current\" : \"\"} />\n </Button>\n }\n />\n <Tooltip.Content>\n {feedback === \"down\" ? \"You found this unhelpful\" : \"Bad response\"}\n </Tooltip.Content>\n </Tooltip.Root>\n </>\n );\n}\n\nexport interface CopyProps {\n content: string;\n}\n\nexport function Copy({ content }: CopyProps) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(content);\n setCopied(true);\n toast.add({\n title: \"Copied!\",\n description: \"Message copied to clipboard\",\n type: \"success\",\n });\n setTimeout(() => setCopied(false), 2000);\n } catch {\n toast.add({\n title: \"Failed to copy\",\n description: \"Could not copy message to clipboard\",\n type: \"error\",\n });\n }\n };\n\n return (\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={handleCopy}\n className=\"h-7 w-7 text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n >\n {copied ? (\n <CheckIcon className=\"text-ppx-green-5\" />\n ) : (\n <CopyIcon />\n )}\n </Button>\n }\n />\n <Tooltip.Content>\n {copied ? \"Copied!\" : \"Copy message\"}\n </Tooltip.Content>\n </Tooltip.Root>\n );\n}\n\nexport interface DebugProps {\n messageId: string;\n debugTrace: unknown;\n}\n\nexport function Debug({ messageId, debugTrace }: DebugProps) {\n const [debugOpen, setDebugOpen] = useState(false);\n\n return (\n <Dialog.Root open={debugOpen} onOpenChange={setDebugOpen}>\n <Tooltip.Root>\n <Tooltip.Trigger\n render={\n <Dialog.Trigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"h-7 w-7 text-ppx-neutral-10 hover:text-ppx-neutral-12\"\n >\n <DebugIcon />\n </Button>\n }\n />\n }\n />\n <Tooltip.Content>\n View debug trace\n </Tooltip.Content>\n </Tooltip.Root>\n\n <Dialog.Portal>\n <Dialog.Overlay />\n <Dialog.Content className=\"max-w-2xl\">\n <Dialog.Header>\n <Dialog.Title>Debug Trace</Dialog.Title>\n <Dialog.Description>\n Response debug information for message {messageId}\n </Dialog.Description>\n </Dialog.Header>\n <div className=\"max-h-96 overflow-auto rounded bg-ppx-neutral-2 p-4\">\n <pre className=\"whitespace-pre-wrap font-mono text-ppx-xs text-ppx-neutral-12\">\n {JSON.stringify(debugTrace, null, 2)}\n </pre>\n </div>\n <Dialog.Footer>\n <Dialog.Close render={<Button variant=\"outline\">Close</Button>} />\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","import ReactMarkdown from \"react-markdown\";\n\nimport type { Message } from \"../../context/xandi-context\";\nimport * as XMessageActions from \"../x-message-actions\";\n\nexport interface MarkdownRendererProps {\n message: Message;\n}\n\nexport function MarkdownRenderer({ message }: MarkdownRendererProps) {\n const isUser = message.role === \"user\";\n const baseClass = `text-ppx-sm leading-relaxed ${isUser ? \"text-white\" : \"text-ppx-neutral-13\"}`;\n const showActions = !isUser;\n\n return (\n <div>\n <div className={baseClass}>\n <ReactMarkdown\n components={{\n p: ({ children }) => <p className=\"mb-2 last:mb-0\">{children}</p>,\n ul: ({ children }) => <ul className=\"mb-2 list-disc pl-4\">{children}</ul>,\n ol: ({ children }) => <ol className=\"mb-2 list-decimal pl-4\">{children}</ol>,\n li: ({ children }) => <li className=\"mb-1\">{children}</li>,\n a: ({ href, children }) => (\n <a href={href} className=\"underline\" target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n ),\n strong: ({ children }) => <strong className=\"font-semibold\">{children}</strong>,\n }}\n >\n {message.content}\n </ReactMarkdown>\n </div>\n {showActions && (\n <div className=\"mt-2\">\n <XMessageActions.Root>\n <XMessageActions.Feedback messageId={message.id} />\n <XMessageActions.Copy content={message.content} />\n {message.debugTrace != null && (\n <XMessageActions.Debug messageId={message.id} debugTrace={message.debugTrace} />\n )}\n </XMessageActions.Root>\n </div>\n )}\n </div>\n );\n}\n","import type { Message } from \"../../context/xandi-context\";\nimport * as XMessageActions from \"../x-message-actions\";\n\nexport interface TextRendererProps {\n message: Message;\n}\n\nexport function TextRenderer({ message }: TextRendererProps) {\n const isUser = message.role === \"user\";\n const showActions = !isUser;\n\n return (\n <div>\n <p className={`text-ppx-sm leading-relaxed ${isUser ? \"text-white\" : \"text-ppx-neutral-13\"}`}>\n {message.content}\n </p>\n {showActions && (\n <div className=\"mt-2\">\n <XMessageActions.Root>\n <XMessageActions.Feedback messageId={message.id} />\n <XMessageActions.Copy content={message.content} />\n {message.debugTrace != null && (\n <XMessageActions.Debug messageId={message.id} debugTrace={message.debugTrace} />\n )}\n </XMessageActions.Root>\n </div>\n )}\n </div>\n );\n}\n","import type { Message, MessageType } from \"../context/xandi-context\";\nimport { MarkdownRenderer } from \"./renderers/markdown-renderer\";\nimport { TextRenderer } from \"./renderers/text-renderer\";\n\nexport interface XMessageItemProps {\n message: Message;\n}\n\nexport function XMessageItem({ message }: XMessageItemProps) {\n const isUser = message.role === \"user\";\n const messageType: MessageType = message.type ?? \"markdown\";\n\n return (\n <div className={`flex ${isUser ? \"justify-end\" : \"justify-start\"}`}>\n {isUser ? (\n <div className=\"max-w-[90%] rounded-2xl rounded-br-sm bg-ppx-green-5 px-4 py-2.5\">\n <MessageRenderer type={messageType} message={message} />\n </div>\n ) : (\n <div className=\"max-w-[90%]\">\n <MessageRenderer type={messageType} message={message} />\n </div>\n )}\n </div>\n );\n}\n\ninterface MessageRendererProps {\n type: MessageType;\n message: Message;\n}\n\nfunction MessageRenderer({ type, message }: MessageRendererProps) {\n switch (type) {\n case \"text\":\n return <TextRenderer message={message} />;\n case \"markdown\":\n default:\n return <MarkdownRenderer message={message} />;\n }\n}\n","import { Avatar } from \"@px-ui/core\";\n\nimport { useXandi } from \"../context/xandi-context\";\n\nexport function XTypingIndicator() {\n const { config } = useXandi();\n\n return (\n <div className=\"flex items-center gap-4\">\n <div className=\"animate-[popUp_0.3s_ease-out_forwards]\">\n <Avatar\n imgSrc={config.avatarUrl}\n name={config.assistantName}\n variant=\"rounded\"\n size=\"48px\"\n hideTooltip\n className=\"border-2 border-ppx-neutral-4\"\n />\n </div>\n <div className=\"flex animate-[slideIn_0.3s_ease-out_0.2s_forwards] items-center gap-2 rounded-xl bg-ppx-neutral-2 px-[10px] pb-[5px] pt-[10px] opacity-0 shadow-sm\">\n <span className=\"h-[12px] w-[12px] animate-bounce rounded-full bg-ppx-neutral-9 [animation-delay:-0.3s]\" />\n <span className=\"h-[12px] w-[12px] animate-bounce rounded-full bg-ppx-neutral-6 [animation-delay:-0.15s]\" />\n <span className=\"h-[12px] w-[12px] animate-bounce rounded-full bg-ppx-neutral-9\" />\n </div>\n </div>\n );\n}\n","import { useEffect, useLayoutEffect, useRef } from \"react\";\n\nimport { XMessageItem } from \"./x-message-item\";\nimport { XTypingIndicator } from \"./x-typing-indicator\";\nimport { useXandi } from \"../context/xandi-context\";\n\nexport interface XMessageContainerProps {\n height?: string | number;\n onLoadMore?: () => void;\n}\n\nexport function XMessageContainer({ height = 400, onLoadMore }: XMessageContainerProps) {\n const { conversation, isLoading } = useXandi();\n const containerRef = useRef<HTMLDivElement>(null);\n const topSentinelRef = useRef<HTMLDivElement>(null);\n const prevScrollHeightRef = useRef(0);\n const prevScrollTopRef = useRef(0);\n const prevLastMessageIdRef = useRef<string | null>(null);\n const prevMessageCountRef = useRef(0);\n const skipScrollToBottomRef = useRef(false);\n\n const messages = conversation.messages;\n const messageCount = messages.length;\n const lastMessageId = messageCount > 0 ? messages[messageCount - 1].id : null;\n\n useLayoutEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const prevCount = prevMessageCountRef.current;\n const prevLastId = prevLastMessageIdRef.current;\n const isPrependOlder =\n prevCount > 0 &&\n messageCount > prevCount &&\n lastMessageId != null &&\n lastMessageId === prevLastId;\n\n if (isPrependOlder) {\n const prevScrollHeight = prevScrollHeightRef.current;\n const prevScrollTop = prevScrollTopRef.current;\n const newScrollHeight = el.scrollHeight;\n el.scrollTop = Math.max(0, prevScrollTop + (newScrollHeight - prevScrollHeight));\n skipScrollToBottomRef.current = true;\n }\n\n prevScrollHeightRef.current = el.scrollHeight;\n prevScrollTopRef.current = el.scrollTop;\n prevMessageCountRef.current = messageCount;\n prevLastMessageIdRef.current = lastMessageId;\n }, [messageCount, lastMessageId, messages]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n if (skipScrollToBottomRef.current) {\n skipScrollToBottomRef.current = false;\n return;\n }\n el.scrollTop = el.scrollHeight - el.clientHeight;\n }, [conversation.messages, isLoading]);\n\n useEffect(() => {\n if (!onLoadMore) return;\n const sentinel = topSentinelRef.current;\n const container = containerRef.current;\n if (!sentinel || !container) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n const [entry] = entries;\n if (entry?.isIntersecting) {\n onLoadMore();\n }\n },\n {\n root: container,\n rootMargin: \"0px\",\n threshold: 0,\n }\n );\n\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [onLoadMore]);\n\n return (\n <div\n ref={containerRef}\n className=\"flex flex-col overflow-y-auto py-[10px]\"\n style={{ height: typeof height === \"number\" ? `${height}px` : height }}\n >\n <div className=\"flex flex-col-reverse gap-5 p-4\">\n {isLoading && <XTypingIndicator />}\n {[...messages].reverse().map((message) => (\n <XMessageItem key={message.id} message={message} />\n ))}\n <div ref={topSentinelRef} className=\"h-1 shrink-0\" aria-hidden=\"true\" />\n </div>\n </div>\n );\n}\n","import { Avatar } from \"@px-ui/core\";\n\nimport { SparklesIcon } from \"../assets/icons\";\nimport { useXandi } from \"../context/xandi-context\";\n\nexport interface XWelcomeProps {\n message: string;\n}\n\nexport function XWelcome({ message }: XWelcomeProps) {\n const { config } = useXandi();\n\n return (\n <div className=\"flex flex-col items-center justify-center gap-4 py-12\">\n <div className=\"relative\">\n <div className=\"absolute -inset-1 rounded-full bg-gradient-to-b from-ppx-green-4 via-ppx-green-5/50 to-transparent\" />\n <div className=\"relative rounded-full bg-ppx-neutral-18 p-1\">\n <Avatar\n imgSrc={config.avatarUrl}\n name={config.assistantName}\n variant=\"rounded\"\n size=\"120px\"\n hideTooltip\n />\n </div>\n\n <div className=\"absolute -bottom-2 left-1/2\">\n <div className=\"flex h-6 w-6 animate-[pulse-zoom_2s_ease-in-out_infinite] items-center justify-center rounded-full bg-ppx-green-5\">\n <SparklesIcon className=\"text-white\" />\n </div>\n </div>\n </div>\n\n <h2 className=\"text-ppx-xl font-semibold text-ppx-foreground\">\n {message}\n </h2>\n </div>\n );\n}\n","import { useEffect } from \"react\";\nimport { XMainIntake, type Suggestion } from \"./x-main-intake\";\nimport { XMessageContainer } from \"./x-message-container\";\nimport { XWelcome } from \"./x-welcome\";\nimport { useXandi } from \"../context/xandi-context\";\nimport type { XandiUIMode } from \"../context/xandi-context\";\n\nexport interface XandiProps {\n welcomeMessage?: string;\n suggestions?: Suggestion[];\n uiMode?: XandiUIMode;\n}\n\nexport function Xandi({\n welcomeMessage = \"How can I help you today?\",\n suggestions = [],\n uiMode,\n}: XandiProps) {\n const { conversation, setUiModeOverride } = useXandi();\n\n useEffect(() => {\n setUiModeOverride(uiMode ?? null);\n return () => setUiModeOverride(null);\n }, [uiMode, setUiModeOverride]);\n const isEmpty = conversation.messages.length === 0;\n\n return (\n <div className=\"flex flex-col\">\n {isEmpty ? (\n <XWelcome message={welcomeMessage} />\n ) : (\n <XMessageContainer />\n )}\n <XMainIntake suggestions={isEmpty ? suggestions : []} />\n </div>\n );\n}\n","import { Avatar, Button } from \"@px-ui/core\";\n\nimport { CloseIcon, MenuIcon, NewChatIcon } from \"../assets/icons\";\nimport { useXandi } from \"../context/xandi-context\";\n\nexport interface XHeaderProps {\n onClose?: () => void;\n onToggleHistory?: () => void;\n}\n\nexport function XHeader({\n onClose,\n onToggleHistory,\n}: XHeaderProps) {\n const { startNewConversation, config } = useXandi();\n\n return (\n <header className=\"flex items-center justify-between border-b border-ppx-neutral-5 bg-transparent px-3 py-2\">\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onToggleHistory}\n aria-label=\"Toggle chat history\"\n >\n <MenuIcon />\n </Button>\n\n <div className=\"flex items-center gap-2\">\n <Avatar\n imgSrc={config.avatarUrl}\n name={config.assistantName}\n variant=\"rounded\"\n size=\"24px\"\n hideTooltip\n />\n <span className=\"font-medium text-ppx-foreground\">{config.assistantName}</span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={startNewConversation}\n aria-label=\"New chat\"\n >\n <NewChatIcon />\n </Button>\n\n {config.uiMode !== \"full\" && (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <CloseIcon />\n </Button>\n )}\n </div>\n </header>\n );\n}\n","import { Button, Spinner } from \"@px-ui/core\";\n\nimport { ChatIcon } from \"../assets/icons\";\n\nexport interface ChatHistoryItem {\n id: string;\n title: string;\n timestamp: Date;\n}\n\nexport interface XChatHistoryProps {\n items?: ChatHistoryItem[];\n isLoading?: boolean;\n activeChatId?: string;\n onSelectChat?: (chatId: string) => void;\n}\n\nexport function XChatHistory({\n items = [],\n isLoading = false,\n activeChatId,\n onSelectChat,\n}: XChatHistoryProps) {\n return (\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"px-3 py-2\">\n <div className=\"flex items-center gap-2 text-ppx-sm font-medium text-ppx-foreground\">\n <ChatIcon />\n Chats\n </div>\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center gap-2 px-6 py-8\">\n <Spinner size=\"medium\" className=\"text-ppx-neutral-10\" />\n <span className=\"text-ppx-sm text-ppx-neutral-10\">Loading conversations...</span>\n </div>\n ) : items.length === 0 ? (\n <div className=\"px-6 py-4 text-ppx-sm text-ppx-neutral-10\">\n No chat history yet\n </div>\n ) : (\n <div className=\"space-y-0.5\">\n {items.map((item) => (\n <Button\n key={item.id}\n variant=\"ghost\"\n onClick={() => onSelectChat?.(item.id)}\n className={`w-full justify-start truncate rounded-none px-6 ${\n activeChatId === item.id\n ? \"bg-ppx-neutral-4 text-ppx-foreground\"\n : \"text-ppx-neutral-12\"\n }`}\n >\n {item.title}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n","import { useEffect, useRef, useState } from \"react\";\nimport { Button } from \"@px-ui/core\";\n\nimport { CloseIcon, NewChatIcon } from \"../assets/icons\";\nimport { useXandi } from \"../context/xandi-context\";\nimport { XChatHistory, type ChatHistoryItem } from \"./x-chat-history\";\n\nexport interface XSidebarProps {\n isOpen?: boolean;\n onClose?: () => void;\n}\n\nexport function XSidebar({\n isOpen = true,\n onClose,\n}: XSidebarProps) {\n const { startNewConversation, getConvHistory, loadConversation, conversation } = useXandi();\n const [chatHistoryItems, setChatHistoryItems] = useState<ChatHistoryItem[]>([]);\n const [isLoadingHistory, setIsLoadingHistory] = useState(false);\n const fetchInProgressRef = useRef(false);\n\n useEffect(() => {\n if (!isOpen || !getConvHistory || fetchInProgressRef.current) return;\n\n fetchInProgressRef.current = true;\n setIsLoadingHistory(true);\n\n getConvHistory()\n .then((history) => {\n setChatHistoryItems(\n (history ?? []).map((item) => ({\n id: item.id,\n title: item.title,\n timestamp: item.timestamp,\n }))\n );\n })\n .catch((error) => {\n console.error(\"Failed to fetch conversation history:\", error);\n })\n .finally(() => {\n fetchInProgressRef.current = false;\n setIsLoadingHistory(false);\n });\n }, [isOpen, getConvHistory]);\n\n const handleSelectChat = (chatId: string) => {\n loadConversation(chatId);\n };\n\n if (!isOpen) return null;\n\n return (\n <aside className=\"flex h-full w-64 flex-col border-r border-ppx-neutral-5 bg-ppx-neutral-2\">\n <div className=\"flex items-center justify-between border-b border-ppx-neutral-5 p-3\">\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onClose}\n aria-label=\"Close sidebar\"\n >\n <CloseIcon />\n </Button>\n </div>\n\n <div className=\"p-3\">\n <Button\n variant=\"ghost\"\n onClick={startNewConversation}\n className=\"w-full justify-start gap-3\"\n >\n <NewChatIcon className=\"h-5 w-5\" />\n New chat\n </Button>\n </div>\n\n <XChatHistory\n items={chatHistoryItems}\n isLoading={isLoadingHistory}\n activeChatId={conversation.id ?? undefined}\n onSelectChat={handleSelectChat}\n />\n </aside>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@px-ui/ai",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "AI components built on top of px-ui-core",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",