@usecrow/ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,203 +1,1179 @@
1
- import { createContext, useRef, useEffect, useMemo, useContext, useState, useCallback } from 'react';
2
- import { CrowClient } from '@usecrow/client';
1
+ import React3, { createContext, forwardRef, useRef, useCallback, useEffect, useContext, useState, useMemo, useLayoutEffect } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
- import { clsx } from 'clsx';
4
+ import { Square, ArrowUp, ChevronDown, Check, MessageCircle, Plus, History, ChevronUp, Brain, ChevronRight, Loader2, X } from 'lucide-react';
5
+ import ReactMarkdown from 'react-markdown';
6
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
7
+ import { CrowClient } from '@usecrow/client';
5
8
 
6
- var CrowContext = createContext(null);
7
- function CrowProvider({
8
- children,
9
+ // src/utils.ts
10
+ var cn = (...classes) => classes.filter(Boolean).join(" ");
11
+ var formatTime = (date) => date.toLocaleTimeString("en-US", {
12
+ hour: "numeric",
13
+ minute: "2-digit"
14
+ });
15
+ var formatConversationDate = (dateStr) => {
16
+ const date = new Date(dateStr);
17
+ const now = /* @__PURE__ */ new Date();
18
+ const diffDays = Math.floor(
19
+ (now.getTime() - date.getTime()) / (1e3 * 60 * 60 * 24)
20
+ );
21
+ if (diffDays === 0) return "Today";
22
+ if (diffDays === 1) return "Yesterday";
23
+ if (diffDays < 7) return `${diffDays} days ago`;
24
+ return date.toLocaleDateString();
25
+ };
26
+ var generateMessageId = (prefix) => `${prefix}-${Date.now()}`;
27
+
28
+ // src/constants.ts
29
+ var AVAILABLE_MODELS = [
30
+ { id: "gpt-4o", name: "GPT-4o", provider: "OpenAI" },
31
+ { id: "gpt-5-mini", name: "GPT-5 Mini", provider: "OpenAI" },
32
+ { id: "gpt-5.2", name: "GPT-5.2", provider: "OpenAI" },
33
+ { id: "gpt-5.2-pro", name: "GPT-5.2 Pro", provider: "OpenAI" },
34
+ { id: "claude-haiku-4-5-20251001", name: "Haiku 4.5", provider: "Anthropic" },
35
+ { id: "claude-sonnet-4-5-20250929", name: "Sonnet 4.5", provider: "Anthropic" },
36
+ { id: "claude-opus-4-5-20251203", name: "Opus 4.5", provider: "Anthropic" }
37
+ ];
38
+ var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
39
+ var DEFAULT_WELCOME_MESSAGE = "Hi! How can I help you today?";
40
+ var MESSAGES_CONTAINER_ID = "crow-messages-container";
41
+
42
+ // src/hooks/useChat.ts
43
+ var getConversationStorageKey = (productId) => `crow_conv_${productId}`;
44
+ function useChat({
9
45
  productId,
10
- apiUrl,
11
- model
46
+ apiUrl = "",
47
+ onVerificationStatus,
48
+ onConversationId,
49
+ onWorkflowEvent,
50
+ onToolCall,
51
+ onRestoredConversation
12
52
  }) {
13
- const clientRef = useRef(null);
14
- if (!clientRef.current) {
15
- clientRef.current = new CrowClient({ productId, apiUrl, model });
16
- }
17
- const client = clientRef.current;
53
+ const [messages, setMessages] = useState([
54
+ {
55
+ id: "welcome",
56
+ content: DEFAULT_WELCOME_MESSAGE,
57
+ isBot: true,
58
+ timestamp: /* @__PURE__ */ new Date()
59
+ }
60
+ ]);
61
+ const [isLoading, setIsLoading] = useState(false);
62
+ const [activeToolCalls, setActiveToolCalls] = useState([]);
63
+ const [conversationId, setConversationId] = useState(() => {
64
+ try {
65
+ return localStorage.getItem(getConversationStorageKey(productId));
66
+ } catch {
67
+ return null;
68
+ }
69
+ });
70
+ const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL);
71
+ const abortControllerRef = useRef(null);
72
+ const hasRestoredRef = useRef(false);
18
73
  useEffect(() => {
19
- return () => {
20
- client.destroy();
21
- };
22
- }, [client]);
23
- const value = useMemo(() => ({ client }), [client]);
24
- return /* @__PURE__ */ jsx(CrowContext.Provider, { value, children });
74
+ if (conversationId && onRestoredConversation && !hasRestoredRef.current) {
75
+ hasRestoredRef.current = true;
76
+ onRestoredConversation(conversationId);
77
+ }
78
+ }, []);
79
+ const streamFromBackend = useCallback(
80
+ async (message, botMsgId) => {
81
+ let accumulatedText = "";
82
+ let firstChunk = true;
83
+ abortControllerRef.current = new AbortController();
84
+ try {
85
+ const identityToken = window.__crow_identity_token;
86
+ const response = await fetch(`${apiUrl}/api/chat/message`, {
87
+ method: "POST",
88
+ headers: { "Content-Type": "application/json" },
89
+ body: JSON.stringify({
90
+ product_id: productId,
91
+ message,
92
+ conversation_id: conversationId,
93
+ identity_token: identityToken,
94
+ model: selectedModel
95
+ }),
96
+ signal: abortControllerRef.current.signal
97
+ });
98
+ if (!response.ok) {
99
+ throw new Error(`HTTP error! status: ${response.status}`);
100
+ }
101
+ const reader = response.body?.getReader();
102
+ const decoder = new TextDecoder();
103
+ if (reader) {
104
+ while (true) {
105
+ const { done, value } = await reader.read();
106
+ if (done) break;
107
+ const chunk = decoder.decode(value);
108
+ const lines = chunk.split("\n");
109
+ for (const line of lines) {
110
+ if (line.startsWith("data: ")) {
111
+ const data = line.slice(6).trim();
112
+ if (data === "[DONE]") {
113
+ setIsLoading(false);
114
+ return;
115
+ }
116
+ try {
117
+ const parsed = JSON.parse(data);
118
+ switch (parsed.type) {
119
+ case "verification_status":
120
+ onVerificationStatus?.(parsed.is_verified === true);
121
+ break;
122
+ case "conversation_id":
123
+ if (parsed.conversation_id) {
124
+ setConversationId(parsed.conversation_id);
125
+ try {
126
+ localStorage.setItem(
127
+ getConversationStorageKey(productId),
128
+ parsed.conversation_id
129
+ );
130
+ } catch {
131
+ }
132
+ onConversationId?.(parsed.conversation_id);
133
+ }
134
+ break;
135
+ case "thinking":
136
+ if (parsed.status === "complete") {
137
+ setMessages(
138
+ (prev) => prev.map(
139
+ (msg) => msg.id === botMsgId ? { ...msg, thinkingComplete: true } : msg
140
+ )
141
+ );
142
+ }
143
+ break;
144
+ case "thinking_token":
145
+ if (parsed.content) {
146
+ setMessages(
147
+ (prev) => prev.map(
148
+ (msg) => msg.id === botMsgId ? { ...msg, thinking: (msg.thinking || "") + parsed.content } : msg
149
+ )
150
+ );
151
+ }
152
+ break;
153
+ case "content":
154
+ if (firstChunk) firstChunk = false;
155
+ accumulatedText += parsed.content;
156
+ setMessages(
157
+ (prev) => prev.map(
158
+ (msg) => msg.id === botMsgId ? { ...msg, content: accumulatedText } : msg
159
+ )
160
+ );
161
+ break;
162
+ case "citations":
163
+ if (parsed.citations) {
164
+ setMessages(
165
+ (prev) => prev.map(
166
+ (msg) => msg.id === botMsgId ? { ...msg, citations: parsed.citations } : msg
167
+ )
168
+ );
169
+ }
170
+ break;
171
+ case "error":
172
+ if (parsed.message) {
173
+ setMessages(
174
+ (prev) => prev.map(
175
+ (msg) => msg.id === botMsgId ? { ...msg, content: parsed.message } : msg
176
+ )
177
+ );
178
+ }
179
+ break;
180
+ case "tool_call_start":
181
+ onToolCall?.({
182
+ type: "start",
183
+ toolName: parsed.tool_name,
184
+ arguments: parsed.arguments
185
+ });
186
+ setActiveToolCalls((prev) => [
187
+ ...prev,
188
+ {
189
+ id: `tool-${Date.now()}`,
190
+ name: parsed.tool_name,
191
+ arguments: parsed.arguments || {},
192
+ status: "executing",
193
+ timestamp: /* @__PURE__ */ new Date()
194
+ }
195
+ ]);
196
+ break;
197
+ case "tool_call_complete":
198
+ onToolCall?.({
199
+ type: "complete",
200
+ toolName: parsed.tool_name,
201
+ success: parsed.success
202
+ });
203
+ setActiveToolCalls(
204
+ (prev) => prev.map(
205
+ (tool) => tool.name === parsed.tool_name ? { ...tool, status: parsed.success ? "complete" : "error" } : tool
206
+ )
207
+ );
208
+ break;
209
+ case "client_tool_call":
210
+ onToolCall?.({
211
+ type: "client_call",
212
+ toolName: parsed.tool_name,
213
+ arguments: parsed.arguments
214
+ });
215
+ break;
216
+ case "workflow_started":
217
+ onWorkflowEvent?.({
218
+ type: "started",
219
+ name: parsed.name,
220
+ todos: parsed.todos
221
+ });
222
+ break;
223
+ case "todo_updated":
224
+ onWorkflowEvent?.({
225
+ type: "todo_updated",
226
+ todoId: parsed.id,
227
+ todoStatus: parsed.status
228
+ });
229
+ break;
230
+ case "workflow_ended":
231
+ onWorkflowEvent?.({ type: "ended" });
232
+ break;
233
+ case "workflow_complete_prompt":
234
+ onWorkflowEvent?.({ type: "complete_prompt" });
235
+ break;
236
+ }
237
+ } catch (e) {
238
+ console.error("[Crow] Parse error:", e);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+ } catch (error) {
245
+ if (error instanceof Error && error.name === "AbortError") {
246
+ if (accumulatedText) {
247
+ setMessages(
248
+ (prev) => prev.map(
249
+ (msg) => msg.id === botMsgId ? { ...msg, content: accumulatedText } : msg
250
+ )
251
+ );
252
+ } else {
253
+ setMessages((prev) => prev.filter((msg) => msg.id !== botMsgId));
254
+ }
255
+ return;
256
+ }
257
+ console.error("[Crow] Error:", error);
258
+ setMessages(
259
+ (prev) => prev.map(
260
+ (msg) => msg.id === botMsgId ? { ...msg, content: "Sorry, I encountered an error. Please try again." } : msg
261
+ )
262
+ );
263
+ } finally {
264
+ setIsLoading(false);
265
+ abortControllerRef.current = null;
266
+ }
267
+ },
268
+ [apiUrl, productId, conversationId, selectedModel, onVerificationStatus, onConversationId, onWorkflowEvent, onToolCall]
269
+ );
270
+ const sendMessage = useCallback(
271
+ (content) => {
272
+ if (!content.trim()) {
273
+ return { userMsgId: "", botMsgId: "" };
274
+ }
275
+ setActiveToolCalls([]);
276
+ const userMsgId = generateMessageId("user");
277
+ const botMsgId = generateMessageId("bot");
278
+ setMessages((prev) => [
279
+ ...prev,
280
+ {
281
+ id: userMsgId,
282
+ content,
283
+ isBot: false,
284
+ timestamp: /* @__PURE__ */ new Date()
285
+ }
286
+ ]);
287
+ setIsLoading(true);
288
+ setMessages((prev) => [
289
+ ...prev,
290
+ {
291
+ id: botMsgId,
292
+ content: "Thinking...",
293
+ isBot: true,
294
+ timestamp: /* @__PURE__ */ new Date()
295
+ }
296
+ ]);
297
+ streamFromBackend(content, botMsgId);
298
+ return { userMsgId, botMsgId };
299
+ },
300
+ [streamFromBackend]
301
+ );
302
+ const stopGeneration = useCallback(() => {
303
+ if (abortControllerRef.current) {
304
+ abortControllerRef.current.abort();
305
+ setIsLoading(false);
306
+ }
307
+ }, []);
308
+ const resetMessages = useCallback(() => {
309
+ setMessages([
310
+ {
311
+ id: "welcome",
312
+ content: DEFAULT_WELCOME_MESSAGE,
313
+ isBot: true,
314
+ timestamp: /* @__PURE__ */ new Date()
315
+ }
316
+ ]);
317
+ setConversationId(null);
318
+ try {
319
+ localStorage.removeItem(getConversationStorageKey(productId));
320
+ } catch {
321
+ }
322
+ }, [productId]);
323
+ const loadMessages = useCallback((historyMessages) => {
324
+ setMessages(historyMessages);
325
+ }, []);
326
+ return {
327
+ messages,
328
+ isLoading,
329
+ activeToolCalls,
330
+ conversationId,
331
+ selectedModel,
332
+ setSelectedModel,
333
+ setConversationId,
334
+ sendMessage,
335
+ stopGeneration,
336
+ resetMessages,
337
+ loadMessages
338
+ };
25
339
  }
26
- function useCrowClient() {
27
- const context = useContext(CrowContext);
28
- if (!context) {
29
- throw new Error("useCrowClient must be used within a CrowProvider");
30
- }
31
- return context.client;
340
+ function useConversations({ productId, apiUrl = "" }) {
341
+ const [conversations, setConversations] = useState([]);
342
+ const [isLoadingHistory, setIsLoadingHistory] = useState(false);
343
+ const loadConversations = useCallback(async () => {
344
+ const token = window.__crow_identity_token;
345
+ if (!token) return;
346
+ try {
347
+ const res = await fetch(
348
+ `${apiUrl}/api/chat/conversations?product_id=${productId}&identity_token=${encodeURIComponent(token)}`
349
+ );
350
+ if (res.ok) {
351
+ const data = await res.json();
352
+ setConversations(data.conversations || []);
353
+ }
354
+ } catch (error) {
355
+ console.error("[Crow] Failed to load conversations:", error);
356
+ }
357
+ }, [apiUrl, productId]);
358
+ const loadConversationHistory = useCallback(
359
+ async (conversationId) => {
360
+ const token = window.__crow_identity_token;
361
+ if (!token) return [];
362
+ setIsLoadingHistory(true);
363
+ try {
364
+ const res = await fetch(
365
+ `${apiUrl}/api/chat/conversations/${conversationId}/history?product_id=${productId}&identity_token=${encodeURIComponent(token)}`
366
+ );
367
+ if (res.ok) {
368
+ const data = await res.json();
369
+ const historyMessages = data.messages || [];
370
+ return historyMessages.map(
371
+ (msg, idx) => ({
372
+ id: `history-${idx}`,
373
+ content: msg.content,
374
+ isBot: msg.role === "assistant",
375
+ timestamp: /* @__PURE__ */ new Date()
376
+ })
377
+ );
378
+ }
379
+ } catch (error) {
380
+ console.error("[Crow] Error loading conversation history:", error);
381
+ } finally {
382
+ setIsLoadingHistory(false);
383
+ }
384
+ return [];
385
+ },
386
+ [apiUrl, productId]
387
+ );
388
+ const loadAnonymousConversationHistory = useCallback(
389
+ async (conversationId) => {
390
+ setIsLoadingHistory(true);
391
+ try {
392
+ const res = await fetch(
393
+ `${apiUrl}/api/chat/conversations/${conversationId}/history/anonymous?product_id=${productId}`
394
+ );
395
+ if (res.ok) {
396
+ const data = await res.json();
397
+ const historyMessages = data.messages || [];
398
+ return historyMessages.map(
399
+ (msg, idx) => ({
400
+ id: `history-${idx}`,
401
+ content: msg.content,
402
+ isBot: msg.role === "assistant",
403
+ timestamp: /* @__PURE__ */ new Date()
404
+ })
405
+ );
406
+ }
407
+ } catch (error) {
408
+ console.error("[Crow] Error loading anonymous conversation history:", error);
409
+ } finally {
410
+ setIsLoadingHistory(false);
411
+ }
412
+ return [];
413
+ },
414
+ [apiUrl, productId]
415
+ );
416
+ return {
417
+ conversations,
418
+ isLoadingHistory,
419
+ loadConversations,
420
+ loadConversationHistory,
421
+ loadAnonymousConversationHistory
422
+ };
32
423
  }
33
- function useCrowClientOptional() {
34
- const context = useContext(CrowContext);
35
- return context?.client ?? null;
424
+ function useWorkflow({
425
+ productId,
426
+ apiUrl = "",
427
+ conversationId,
428
+ selectedModel,
429
+ onMessage
430
+ }) {
431
+ const [activeWorkflow, setActiveWorkflow] = useState(null);
432
+ const abortControllerRef = useRef(null);
433
+ const startWorkflow = useCallback((name, todos) => {
434
+ setActiveWorkflow({ name, todos });
435
+ }, []);
436
+ const updateTodo = useCallback((todoId, status) => {
437
+ setActiveWorkflow((prev) => {
438
+ if (!prev) return null;
439
+ return {
440
+ ...prev,
441
+ todos: prev.todos.map(
442
+ (todo) => todo.id === todoId ? { ...todo, status } : todo
443
+ )
444
+ };
445
+ });
446
+ }, []);
447
+ const markComplete = useCallback(() => {
448
+ setActiveWorkflow((prev) => {
449
+ if (!prev) return null;
450
+ return { ...prev, isComplete: true };
451
+ });
452
+ }, []);
453
+ const endWorkflow = useCallback((delay = 0) => {
454
+ if (delay > 0) {
455
+ setTimeout(() => setActiveWorkflow(null), delay);
456
+ } else {
457
+ setActiveWorkflow(null);
458
+ }
459
+ }, []);
460
+ const exitWorkflow = useCallback(async () => {
461
+ abortControllerRef.current = new AbortController();
462
+ try {
463
+ const identityToken = window.__crow_identity_token;
464
+ const response = await fetch(`${apiUrl}/api/chat/message`, {
465
+ method: "POST",
466
+ headers: { "Content-Type": "application/json" },
467
+ body: JSON.stringify({
468
+ product_id: productId,
469
+ message: "[EXIT_WORKFLOW]",
470
+ conversation_id: conversationId,
471
+ identity_token: identityToken,
472
+ model: selectedModel
473
+ }),
474
+ signal: abortControllerRef.current.signal
475
+ });
476
+ if (!response.ok) return;
477
+ const reader = response.body?.getReader();
478
+ const decoder = new TextDecoder();
479
+ if (reader) {
480
+ while (true) {
481
+ const { done, value } = await reader.read();
482
+ if (done) break;
483
+ const chunk = decoder.decode(value);
484
+ const lines = chunk.split("\n");
485
+ for (const line of lines) {
486
+ if (line.startsWith("data: ")) {
487
+ const data = line.slice(6).trim();
488
+ if (data === "[DONE]") return;
489
+ try {
490
+ const parsed = JSON.parse(data);
491
+ if (parsed.type === "workflow_ended") {
492
+ setActiveWorkflow(null);
493
+ }
494
+ if (parsed.type === "content" && parsed.content) {
495
+ onMessage?.(parsed.content);
496
+ }
497
+ } catch {
498
+ }
499
+ }
500
+ }
501
+ }
502
+ }
503
+ } catch (error) {
504
+ console.error("[Crow] Exit workflow error:", error);
505
+ } finally {
506
+ abortControllerRef.current = null;
507
+ }
508
+ }, [apiUrl, productId, conversationId, selectedModel, onMessage]);
509
+ return {
510
+ activeWorkflow,
511
+ startWorkflow,
512
+ updateTodo,
513
+ markComplete,
514
+ endWorkflow,
515
+ exitWorkflow
516
+ };
36
517
  }
37
- function useChat({ client, onStreamEvent }) {
38
- const [messages, setMessages] = useState(client.messages);
39
- const [isLoading, setIsLoading] = useState(client.isLoading);
40
- const onStreamEventRef = useRef(onStreamEvent);
518
+ function useCrowAPI({ onIdentified, onReset } = {}) {
519
+ const onIdentifiedRef = useRef(onIdentified);
520
+ const onResetRef = useRef(onReset);
521
+ const hasCalledInitialRef = useRef(false);
41
522
  useEffect(() => {
42
- onStreamEventRef.current = onStreamEvent;
523
+ onIdentifiedRef.current = onIdentified;
524
+ onResetRef.current = onReset;
43
525
  });
44
526
  useEffect(() => {
45
- const unsubMessages = client.onMessages(setMessages);
46
- const unsubLoading = client.onLoading(setIsLoading);
47
- setMessages(client.messages);
48
- setIsLoading(client.isLoading);
527
+ window.crow = function(command, options) {
528
+ const opts = options;
529
+ switch (command) {
530
+ case "identify":
531
+ if (!opts?.token) {
532
+ console.error("[Crow] identify() requires a token");
533
+ return;
534
+ }
535
+ window.__crow_identity_token = opts.token;
536
+ const { token, ...metadata } = opts;
537
+ window.__crow_public_metadata = metadata;
538
+ console.log("[Crow] User identified");
539
+ window.dispatchEvent(
540
+ new CustomEvent("crow:identified", { detail: { token, metadata } })
541
+ );
542
+ break;
543
+ case "resetUser":
544
+ window.__crow_identity_token = void 0;
545
+ window.__crow_public_metadata = void 0;
546
+ console.log("[Crow] User reset");
547
+ window.dispatchEvent(new CustomEvent("crow:reset"));
548
+ break;
549
+ case "registerTools":
550
+ if (!opts || typeof opts !== "object") {
551
+ console.error("[Crow] registerTools() requires an object");
552
+ return;
553
+ }
554
+ if (!window.__crow_client_tools) {
555
+ window.__crow_client_tools = {};
556
+ }
557
+ for (const [toolName, handler] of Object.entries(opts)) {
558
+ if (typeof handler === "function") {
559
+ window.__crow_client_tools[toolName] = handler;
560
+ console.log(`[Crow] Registered tool: ${toolName}`);
561
+ }
562
+ }
563
+ break;
564
+ case "runJourney":
565
+ if (!opts?.journeyId) {
566
+ console.error("[Crow] runJourney() requires a journeyId");
567
+ return;
568
+ }
569
+ window.dispatchEvent(
570
+ new CustomEvent("crow:runJourney", {
571
+ detail: { journeyId: opts.journeyId, inputs: opts.inputs || {} }
572
+ })
573
+ );
574
+ break;
575
+ default:
576
+ console.warn(`[Crow] Unknown command: ${command}`);
577
+ }
578
+ };
579
+ console.log("[Crow] API ready");
580
+ const handleIdentified = () => onIdentifiedRef.current?.();
581
+ const handleReset = () => onResetRef.current?.();
582
+ window.addEventListener("crow:identified", handleIdentified);
583
+ window.addEventListener("crow:reset", handleReset);
584
+ if (window.__crow_identity_token && !hasCalledInitialRef.current) {
585
+ hasCalledInitialRef.current = true;
586
+ onIdentifiedRef.current?.();
587
+ }
49
588
  return () => {
50
- unsubMessages();
51
- unsubLoading();
589
+ window.removeEventListener("crow:identified", handleIdentified);
590
+ window.removeEventListener("crow:reset", handleReset);
52
591
  };
53
- }, [client]);
54
- const sendMessage = useCallback(
55
- async (content) => {
56
- for await (const event of client.sendMessage(content)) {
57
- onStreamEventRef.current?.(event);
592
+ }, []);
593
+ const executeClientTool = useCallback(
594
+ async (toolName, args) => {
595
+ const handler = window.__crow_client_tools?.[toolName];
596
+ if (handler) {
597
+ try {
598
+ return await handler(args);
599
+ } catch (err) {
600
+ console.error(`[Crow] Client tool error: ${toolName}`, err);
601
+ return { status: "error", error: String(err) };
602
+ }
58
603
  }
604
+ console.warn(`[Crow] No handler for: ${toolName}`);
605
+ return { status: "error", error: "No handler registered" };
59
606
  },
60
- [client]
607
+ []
61
608
  );
62
- const stop = useCallback(() => {
63
- client.stop();
64
- }, [client]);
65
- const clearMessages = useCallback(() => {
66
- client.clearMessages();
67
- }, [client]);
609
+ return { executeClientTool };
610
+ }
611
+
612
+ // src/styles/defaults.ts
613
+ var DEFAULT_WIDGET_STYLES = {
614
+ // ═══════════════════════════════════════════════════════════
615
+ // COLORS
616
+ // ═══════════════════════════════════════════════════════════
617
+ colors: {
618
+ // Primary accent
619
+ primary: "#000000",
620
+ // Widget container
621
+ background: "rgba(255, 255, 255, 0.95)",
622
+ border: "#d1d5db",
623
+ text: "#111827",
624
+ // Bot messages
625
+ botBubble: "#000000",
626
+ botText: "#ffffff",
627
+ // User messages
628
+ userBubble: "#ffffff",
629
+ userText: "#000000",
630
+ userBorder: "#000000",
631
+ // Floating chat bubble
632
+ bubbleBackground: "#ffffff",
633
+ bubbleBorder: "#d1d5db",
634
+ bubbleIcon: "#111827",
635
+ // Messages container
636
+ messagesBackground: "rgba(255, 255, 255, 0.5)"
637
+ },
638
+ // ═══════════════════════════════════════════════════════════
639
+ // TYPOGRAPHY
640
+ // ═══════════════════════════════════════════════════════════
641
+ typography: {
642
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
643
+ fontSize: 14,
644
+ headerFontSize: 16
645
+ },
646
+ // ═══════════════════════════════════════════════════════════
647
+ // ANIMATIONS
648
+ // ═══════════════════════════════════════════════════════════
649
+ animations: {
650
+ duration: 0.3,
651
+ easing: "easeInOut"
652
+ },
653
+ // ═══════════════════════════════════════════════════════════
654
+ // DIMENSIONS
655
+ // ═══════════════════════════════════════════════════════════
656
+ dimensions: {
657
+ width: 400,
658
+ maxHeight: 600,
659
+ previewHeight: 700,
660
+ messagesMaxHeight: 350,
661
+ borderRadius: 24,
662
+ padding: 20
663
+ },
664
+ // ═══════════════════════════════════════════════════════════
665
+ // POSITIONING (floating widget only)
666
+ // ═══════════════════════════════════════════════════════════
667
+ position: {
668
+ right: 16,
669
+ bottom: 80,
670
+ bubbleRight: 16,
671
+ bubbleBottom: 16
672
+ },
673
+ // ═══════════════════════════════════════════════════════════
674
+ // CHAT BUBBLE (floating button)
675
+ // ═══════════════════════════════════════════════════════════
676
+ bubble: {
677
+ size: 48,
678
+ iconSize: 24
679
+ },
680
+ // ═══════════════════════════════════════════════════════════
681
+ // SHADOWS
682
+ // ═══════════════════════════════════════════════════════════
683
+ shadows: {
684
+ widget: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
685
+ bubble: "0 25px 50px -12px rgba(0, 0, 0, 0.25)"
686
+ },
687
+ // ═══════════════════════════════════════════════════════════
688
+ // BRANDING
689
+ // ═══════════════════════════════════════════════════════════
690
+ branding: {
691
+ showPoweredBy: true,
692
+ poweredByText: "Powered by Crow",
693
+ showLogo: true,
694
+ logoUrl: "/static/crow.png"
695
+ }
696
+ };
697
+ var DEFAULT_COPILOT_STYLES = {
698
+ // ═══════════════════════════════════════════════════════════
699
+ // COLORS (same as widget by default)
700
+ // ═══════════════════════════════════════════════════════════
701
+ colors: {
702
+ primary: "#000000",
703
+ background: "rgba(255, 255, 255, 0.95)",
704
+ border: "#d1d5db",
705
+ text: "#111827",
706
+ botBubble: "#000000",
707
+ botText: "#ffffff",
708
+ userBubble: "#ffffff",
709
+ userText: "#000000",
710
+ userBorder: "#000000",
711
+ bubbleBackground: "#ffffff",
712
+ bubbleBorder: "#d1d5db",
713
+ bubbleIcon: "#111827",
714
+ messagesBackground: "rgba(255, 255, 255, 0.5)"
715
+ },
716
+ // ═══════════════════════════════════════════════════════════
717
+ // TYPOGRAPHY
718
+ // ═══════════════════════════════════════════════════════════
719
+ typography: {
720
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
721
+ fontSize: 14,
722
+ headerFontSize: 16
723
+ },
724
+ // ═══════════════════════════════════════════════════════════
725
+ // ANIMATIONS
726
+ // ═══════════════════════════════════════════════════════════
727
+ animations: {
728
+ duration: 0.3,
729
+ easing: "easeInOut"
730
+ },
731
+ // ═══════════════════════════════════════════════════════════
732
+ // DIMENSIONS
733
+ // ═══════════════════════════════════════════════════════════
734
+ dimensions: {
735
+ width: 400,
736
+ headerHeight: 56,
737
+ borderRadius: 16,
738
+ padding: 16
739
+ },
740
+ // ═══════════════════════════════════════════════════════════
741
+ // POSITIONING
742
+ // ═══════════════════════════════════════════════════════════
743
+ position: {
744
+ side: "right"
745
+ },
746
+ // ═══════════════════════════════════════════════════════════
747
+ // BRANDING
748
+ // ═══════════════════════════════════════════════════════════
749
+ branding: {
750
+ showPoweredBy: true,
751
+ poweredByText: "Powered by Crow"
752
+ }
753
+ };
754
+
755
+ // src/styles/utils.ts
756
+ function deepMerge(target, source) {
757
+ if (!source) return target;
758
+ const result = { ...target };
759
+ for (const key of Object.keys(source)) {
760
+ const sourceValue = source[key];
761
+ const targetValue = target[key];
762
+ if (sourceValue !== void 0 && sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && typeof targetValue === "object" && !Array.isArray(targetValue)) {
763
+ result[key] = deepMerge(
764
+ targetValue,
765
+ sourceValue
766
+ );
767
+ } else if (sourceValue !== void 0) {
768
+ result[key] = sourceValue;
769
+ }
770
+ }
771
+ return result;
772
+ }
773
+ function mergeWidgetStyles(dbStyles, propStyles) {
774
+ let result = { ...DEFAULT_WIDGET_STYLES };
775
+ if (dbStyles) {
776
+ result = {
777
+ colors: deepMerge(result.colors, dbStyles.colors),
778
+ typography: deepMerge(result.typography, dbStyles.typography),
779
+ animations: deepMerge(result.animations, dbStyles.animations),
780
+ dimensions: deepMerge(result.dimensions, dbStyles.dimensions),
781
+ position: deepMerge(result.position, dbStyles.position),
782
+ bubble: deepMerge(result.bubble, dbStyles.bubble),
783
+ shadows: deepMerge(result.shadows, dbStyles.shadows),
784
+ branding: deepMerge(result.branding, dbStyles.branding)
785
+ };
786
+ }
787
+ if (propStyles) {
788
+ result = {
789
+ colors: deepMerge(result.colors, propStyles.colors),
790
+ typography: deepMerge(result.typography, propStyles.typography),
791
+ animations: deepMerge(result.animations, propStyles.animations),
792
+ dimensions: deepMerge(result.dimensions, propStyles.dimensions),
793
+ position: deepMerge(result.position, propStyles.position),
794
+ bubble: deepMerge(result.bubble, propStyles.bubble),
795
+ shadows: deepMerge(result.shadows, propStyles.shadows),
796
+ branding: deepMerge(result.branding, propStyles.branding)
797
+ };
798
+ }
799
+ return result;
800
+ }
801
+ function mergeCopilotStyles(dbStyles, propStyles) {
802
+ let result = { ...DEFAULT_COPILOT_STYLES };
803
+ if (dbStyles) {
804
+ result = {
805
+ colors: deepMerge(result.colors, dbStyles.colors),
806
+ typography: deepMerge(result.typography, dbStyles.typography),
807
+ animations: deepMerge(result.animations, dbStyles.animations),
808
+ dimensions: deepMerge(result.dimensions, dbStyles.dimensions),
809
+ position: deepMerge(result.position, dbStyles.position),
810
+ branding: deepMerge(result.branding, dbStyles.branding)
811
+ };
812
+ }
813
+ if (propStyles) {
814
+ result = {
815
+ colors: deepMerge(result.colors, propStyles.colors),
816
+ typography: deepMerge(result.typography, propStyles.typography),
817
+ animations: deepMerge(result.animations, propStyles.animations),
818
+ dimensions: deepMerge(result.dimensions, propStyles.dimensions),
819
+ position: deepMerge(result.position, propStyles.position),
820
+ branding: deepMerge(result.branding, propStyles.branding)
821
+ };
822
+ }
823
+ return result;
824
+ }
825
+ function stylesToCSSVariables(styles2) {
68
826
  return {
69
- messages,
827
+ // Colors
828
+ "--crow-color-primary": styles2.colors.primary,
829
+ "--crow-color-background": styles2.colors.background,
830
+ "--crow-color-text": styles2.colors.text,
831
+ "--crow-color-border": styles2.colors.border,
832
+ "--crow-color-bot-bubble": styles2.colors.botBubble,
833
+ "--crow-color-bot-text": styles2.colors.botText,
834
+ "--crow-color-user-bubble": styles2.colors.userBubble,
835
+ "--crow-color-user-text": styles2.colors.userText,
836
+ "--crow-color-user-border": styles2.colors.userBorder,
837
+ "--crow-color-messages-bg": styles2.colors.messagesBackground,
838
+ // Typography
839
+ "--crow-font-family": styles2.typography.fontFamily,
840
+ "--crow-font-size": `${styles2.typography.fontSize}px`,
841
+ "--crow-header-font-size": `${styles2.typography.headerFontSize}px`,
842
+ // Animations
843
+ "--crow-animation-duration": `${styles2.animations.duration}s`,
844
+ "--crow-animation-easing": styles2.animations.easing
845
+ };
846
+ }
847
+
848
+ // src/hooks/useWidgetStyles.ts
849
+ var styleCache = /* @__PURE__ */ new Map();
850
+ async function fetchWidgetConfig(productId, apiUrl) {
851
+ const baseUrl = apiUrl || "";
852
+ const url = `${baseUrl}/api/products/${productId}/widget-config`;
853
+ const response = await fetch(url);
854
+ if (!response.ok) {
855
+ throw new Error(`Failed to fetch widget config: ${response.status} ${response.statusText}`);
856
+ }
857
+ return response.json();
858
+ }
859
+ function useWidgetStyles({
860
+ productId,
861
+ apiUrl,
862
+ propStyles,
863
+ skip = false,
864
+ cacheKey
865
+ }) {
866
+ const key = cacheKey || productId;
867
+ const [isLoading, setIsLoading] = useState(!skip && !styleCache.has(key));
868
+ const [error, setError] = useState(null);
869
+ const [dbStyles, setDbStyles] = useState(
870
+ styleCache.get(key)?.widgetStyles
871
+ );
872
+ const [agentName, setAgentName] = useState(
873
+ styleCache.get(key)?.agentName || "Assistant"
874
+ );
875
+ const hasFetchedRef = useRef(false);
876
+ const fetchStyles = async () => {
877
+ if (skip) return;
878
+ setIsLoading(true);
879
+ setError(null);
880
+ try {
881
+ const config = await fetchWidgetConfig(productId, apiUrl);
882
+ styleCache.set(key, config);
883
+ setDbStyles(config.widgetStyles);
884
+ setAgentName(config.agentName || "Assistant");
885
+ } catch (err) {
886
+ console.error("[CrowWidget] Failed to fetch styles:", err);
887
+ setError(err instanceof Error ? err : new Error(String(err)));
888
+ } finally {
889
+ setIsLoading(false);
890
+ }
891
+ };
892
+ useEffect(() => {
893
+ if (skip || hasFetchedRef.current) return;
894
+ const cached = styleCache.get(key);
895
+ if (cached) {
896
+ setDbStyles(cached.widgetStyles);
897
+ setAgentName(cached.agentName || "Assistant");
898
+ setIsLoading(false);
899
+ return;
900
+ }
901
+ hasFetchedRef.current = true;
902
+ fetchStyles();
903
+ }, [productId, apiUrl, skip, key]);
904
+ const styles2 = mergeWidgetStyles(dbStyles, propStyles);
905
+ return {
906
+ styles: styles2,
70
907
  isLoading,
71
- sendMessage,
72
- stop,
73
- clearMessages
908
+ error,
909
+ agentName,
910
+ refetch: fetchStyles
74
911
  };
75
912
  }
76
- function ChatBubble({ isOpen, onClick, className }) {
913
+ function useCopilotStyles({
914
+ productId,
915
+ apiUrl,
916
+ propStyles,
917
+ skip = false,
918
+ cacheKey
919
+ }) {
920
+ const key = cacheKey || productId;
921
+ const [isLoading, setIsLoading] = useState(!skip && !styleCache.has(key));
922
+ const [error, setError] = useState(null);
923
+ const [dbStyles, setDbStyles] = useState(
924
+ styleCache.get(key)?.copilotStyles
925
+ );
926
+ const [agentName, setAgentName] = useState(
927
+ styleCache.get(key)?.agentName || "Assistant"
928
+ );
929
+ const hasFetchedRef = useRef(false);
930
+ const fetchStyles = async () => {
931
+ if (skip) return;
932
+ setIsLoading(true);
933
+ setError(null);
934
+ try {
935
+ const config = await fetchWidgetConfig(productId, apiUrl);
936
+ styleCache.set(key, config);
937
+ setDbStyles(config.copilotStyles);
938
+ setAgentName(config.agentName || "Assistant");
939
+ } catch (err) {
940
+ console.error("[CrowCopilot] Failed to fetch styles:", err);
941
+ setError(err instanceof Error ? err : new Error(String(err)));
942
+ } finally {
943
+ setIsLoading(false);
944
+ }
945
+ };
946
+ useEffect(() => {
947
+ if (skip || hasFetchedRef.current) return;
948
+ const cached = styleCache.get(key);
949
+ if (cached) {
950
+ setDbStyles(cached.copilotStyles);
951
+ setAgentName(cached.agentName || "Assistant");
952
+ setIsLoading(false);
953
+ return;
954
+ }
955
+ hasFetchedRef.current = true;
956
+ fetchStyles();
957
+ }, [productId, apiUrl, skip, key]);
958
+ const styles2 = mergeCopilotStyles(dbStyles, propStyles);
959
+ return {
960
+ styles: styles2,
961
+ isLoading,
962
+ error,
963
+ agentName,
964
+ refetch: fetchStyles
965
+ };
966
+ }
967
+ function clearStyleCache(productId) {
968
+ if (productId) {
969
+ styleCache.delete(productId);
970
+ } else {
971
+ styleCache.clear();
972
+ }
973
+ }
974
+ function usePreviewWidgetStyles(previewStyles) {
975
+ return {
976
+ styles: mergeWidgetStyles(void 0, previewStyles)
977
+ };
978
+ }
979
+ function usePreviewCopilotStyles(previewStyles) {
980
+ return {
981
+ styles: mergeCopilotStyles(void 0, previewStyles)
982
+ };
983
+ }
984
+ var WidgetStyleContext = createContext(null);
985
+ function WidgetStyleProvider({
986
+ children,
987
+ styles: styles2,
988
+ agentName = "Assistant",
989
+ isLoading = false,
990
+ variant = "floating"
991
+ }) {
992
+ const value = useMemo(
993
+ () => ({ styles: styles2, agentName, isLoading, variant }),
994
+ [styles2, agentName, isLoading, variant]
995
+ );
996
+ return /* @__PURE__ */ jsx(WidgetStyleContext.Provider, { value, children });
997
+ }
998
+ function useWidgetStyleContext() {
999
+ const context = useContext(WidgetStyleContext);
1000
+ if (!context) {
1001
+ throw new Error(
1002
+ "useWidgetStyleContext must be used within a WidgetStyleProvider. Make sure your component is wrapped with CrowWidget or WidgetStyleProvider."
1003
+ );
1004
+ }
1005
+ return context;
1006
+ }
1007
+ function useWidgetStyles2() {
1008
+ const context = useContext(WidgetStyleContext);
1009
+ return context?.styles ?? DEFAULT_WIDGET_STYLES;
1010
+ }
1011
+ var CopilotStyleContext = createContext(null);
1012
+ function CopilotStyleProvider({
1013
+ children,
1014
+ styles: styles2,
1015
+ agentName = "Assistant",
1016
+ isLoading = false
1017
+ }) {
1018
+ const value = useMemo(
1019
+ () => ({ styles: styles2, agentName, isLoading }),
1020
+ [styles2, agentName, isLoading]
1021
+ );
1022
+ return /* @__PURE__ */ jsx(CopilotStyleContext.Provider, { value, children });
1023
+ }
1024
+ function useCopilotStyleContext() {
1025
+ const context = useContext(CopilotStyleContext);
1026
+ if (!context) {
1027
+ throw new Error(
1028
+ "useCopilotStyleContext must be used within a CopilotStyleProvider. Make sure your component is wrapped with CrowCopilot or CopilotStyleProvider."
1029
+ );
1030
+ }
1031
+ return context;
1032
+ }
1033
+ function useCopilotStyles2() {
1034
+ const context = useContext(CopilotStyleContext);
1035
+ return context?.styles ?? DEFAULT_COPILOT_STYLES;
1036
+ }
1037
+ function ChatBubble({ isExpanded, onClick }) {
1038
+ const { styles: styles2 } = useWidgetStyleContext();
77
1039
  return /* @__PURE__ */ jsx(
78
1040
  "button",
79
1041
  {
80
1042
  onClick,
81
- className: clsx(
82
- "crow-fixed crow-z-[999999] crow-rounded-full",
83
- "crow-w-14 crow-h-14 crow-flex crow-items-center crow-justify-center",
84
- "crow-bg-crow-primary hover:crow-bg-crow-primary-dark",
85
- "crow-shadow-lg hover:crow-shadow-xl crow-transition-all crow-duration-300",
86
- "hover:crow-scale-110",
87
- className
88
- ),
1043
+ className: "fixed z-[999999] rounded-full flex items-center justify-center shadow-2xl hover:scale-110 transition-all duration-500 border border-gray-200/30 backdrop-blur-md",
89
1044
  style: {
90
- right: "20px",
91
- bottom: "20px"
1045
+ width: styles2.bubble.size,
1046
+ height: styles2.bubble.size,
1047
+ right: styles2.position.bubbleRight,
1048
+ bottom: styles2.position.bubbleBottom,
1049
+ background: styles2.colors.bubbleBackground,
1050
+ borderColor: styles2.colors.bubbleBorder,
1051
+ boxShadow: styles2.shadows.bubble,
1052
+ color: styles2.colors.bubbleIcon
92
1053
  },
93
- "aria-label": isOpen ? "Close chat" : "Open chat",
94
- children: isOpen ? /* @__PURE__ */ jsx(
95
- "svg",
96
- {
97
- className: "crow-w-6 crow-h-6 crow-text-white",
98
- fill: "none",
99
- stroke: "currentColor",
100
- viewBox: "0 0 24 24",
101
- children: /* @__PURE__ */ jsx(
102
- "path",
103
- {
104
- strokeLinecap: "round",
105
- strokeLinejoin: "round",
106
- strokeWidth: 2,
107
- d: "M19 9l-7 7-7-7"
108
- }
109
- )
110
- }
111
- ) : /* @__PURE__ */ jsx(
112
- "svg",
113
- {
114
- className: "crow-w-6 crow-h-6 crow-text-white",
115
- fill: "none",
116
- stroke: "currentColor",
117
- viewBox: "0 0 24 24",
118
- children: /* @__PURE__ */ jsx(
119
- "path",
120
- {
121
- strokeLinecap: "round",
122
- strokeLinejoin: "round",
123
- strokeWidth: 2,
124
- d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
125
- }
126
- )
127
- }
128
- )
1054
+ "aria-label": isExpanded ? "Close Chat" : "Open Chat",
1055
+ children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { size: styles2.bubble.iconSize, strokeWidth: 2 }) : /* @__PURE__ */ jsx(MessageCircle, { size: styles2.bubble.iconSize, strokeWidth: 2 })
129
1056
  }
130
1057
  );
131
1058
  }
1059
+ var GlassCard = forwardRef(
1060
+ ({ className, children, ...props }, ref) => {
1061
+ return /* @__PURE__ */ jsx(
1062
+ "div",
1063
+ {
1064
+ ref,
1065
+ className: cn(
1066
+ "bg-white/30 border border-gray-200/30 flex flex-col gap-6 rounded-2xl py-6 backdrop-blur-md",
1067
+ className
1068
+ ),
1069
+ ...props,
1070
+ children
1071
+ }
1072
+ );
1073
+ }
1074
+ );
1075
+ GlassCard.displayName = "GlassCard";
1076
+ var WidgetShell = forwardRef(
1077
+ ({ children, className }, ref) => {
1078
+ const { styles: styles2, variant } = useWidgetStyleContext();
1079
+ const baseStyle = {
1080
+ borderRadius: styles2.dimensions.borderRadius,
1081
+ padding: styles2.dimensions.padding,
1082
+ background: styles2.colors.background,
1083
+ borderColor: styles2.colors.border,
1084
+ color: styles2.colors.text,
1085
+ boxShadow: styles2.shadows.widget,
1086
+ fontFamily: styles2.typography.fontFamily,
1087
+ fontSize: styles2.typography.fontSize
1088
+ };
1089
+ if (variant === "embedded") {
1090
+ return /* @__PURE__ */ jsx(
1091
+ GlassCard,
1092
+ {
1093
+ ref,
1094
+ className: `flex flex-col shadow-2xl gap-3 w-full h-full ${className ?? ""}`,
1095
+ style: {
1096
+ ...baseStyle,
1097
+ maxWidth: styles2.dimensions.width,
1098
+ maxHeight: styles2.dimensions.previewHeight
1099
+ },
1100
+ children
1101
+ }
1102
+ );
1103
+ }
1104
+ return /* @__PURE__ */ jsx(
1105
+ GlassCard,
1106
+ {
1107
+ ref,
1108
+ className: `fixed z-[999999] shadow-2xl gap-3 transition-all duration-500 flex flex-col ${className ?? ""}`,
1109
+ style: {
1110
+ ...baseStyle,
1111
+ width: `min(${styles2.dimensions.width}px, calc(100vw - 32px))`,
1112
+ height: `min(${styles2.dimensions.maxHeight}px, calc(100vh - 120px))`,
1113
+ right: styles2.position.right,
1114
+ bottom: styles2.position.bottom
1115
+ },
1116
+ children
1117
+ }
1118
+ );
1119
+ }
1120
+ );
1121
+ WidgetShell.displayName = "WidgetShell";
132
1122
  function WidgetHeader({
133
- title = "Chat",
1123
+ isVerifiedUser,
1124
+ showConversationList,
134
1125
  onNewChat,
135
- onClose,
136
- showNewChat = true,
137
- showClose = false,
138
- className
1126
+ onToggleHistory,
1127
+ showMinimize = false,
1128
+ isMinimized = false,
1129
+ onToggleMinimize
139
1130
  }) {
1131
+ const { agentName, styles: styles2 } = useWidgetStyleContext();
140
1132
  return /* @__PURE__ */ jsxs(
141
1133
  "div",
142
1134
  {
143
- className: clsx(
144
- "crow-flex crow-items-center crow-justify-between crow-px-4 crow-py-3",
145
- "crow-border-b crow-border-gray-200 crow-bg-white",
146
- className
147
- ),
1135
+ className: "flex items-center justify-between mb-3 pb-2 border-b",
1136
+ style: { borderColor: styles2.colors.border },
148
1137
  children: [
149
- /* @__PURE__ */ jsx("h2", { className: "crow-font-semibold crow-text-gray-900", children: title }),
150
- /* @__PURE__ */ jsxs("div", { className: "crow-flex crow-items-center crow-gap-2", children: [
151
- showNewChat && onNewChat && /* @__PURE__ */ jsx(
1138
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
1139
+ "span",
1140
+ {
1141
+ className: "text-sm font-semibold",
1142
+ style: {
1143
+ color: styles2.colors.text,
1144
+ fontSize: styles2.typography.headerFontSize
1145
+ },
1146
+ children: agentName
1147
+ }
1148
+ ) }),
1149
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1150
+ /* @__PURE__ */ jsx(
152
1151
  "button",
153
1152
  {
154
1153
  onClick: onNewChat,
155
- className: "crow-p-1.5 crow-rounded-lg crow-text-gray-500 hover:crow-bg-gray-100 hover:crow-text-gray-700 crow-transition-colors",
156
- "aria-label": "New chat",
157
- children: /* @__PURE__ */ jsx(
158
- "svg",
159
- {
160
- className: "crow-w-5 crow-h-5",
161
- fill: "none",
162
- stroke: "currentColor",
163
- viewBox: "0 0 24 24",
164
- children: /* @__PURE__ */ jsx(
165
- "path",
166
- {
167
- strokeLinecap: "round",
168
- strokeLinejoin: "round",
169
- strokeWidth: 2,
170
- d: "M12 4v16m8-8H4"
171
- }
172
- )
173
- }
174
- )
1154
+ className: "p-1.5 hover:bg-gray-200 rounded transition-colors",
1155
+ "aria-label": "New Chat",
1156
+ title: "New Chat",
1157
+ children: /* @__PURE__ */ jsx(Plus, { size: 18, className: "text-gray-700" })
175
1158
  }
176
1159
  ),
177
- showClose && onClose && /* @__PURE__ */ jsx(
1160
+ isVerifiedUser && /* @__PURE__ */ jsx(
178
1161
  "button",
179
1162
  {
180
- onClick: onClose,
181
- className: "crow-p-1.5 crow-rounded-lg crow-text-gray-500 hover:crow-bg-gray-100 hover:crow-text-gray-700 crow-transition-colors",
182
- "aria-label": "Close",
183
- children: /* @__PURE__ */ jsx(
184
- "svg",
185
- {
186
- className: "crow-w-5 crow-h-5",
187
- fill: "none",
188
- stroke: "currentColor",
189
- viewBox: "0 0 24 24",
190
- children: /* @__PURE__ */ jsx(
191
- "path",
192
- {
193
- strokeLinecap: "round",
194
- strokeLinejoin: "round",
195
- strokeWidth: 2,
196
- d: "M6 18L18 6M6 6l12 12"
197
- }
198
- )
199
- }
200
- )
1163
+ onClick: onToggleHistory,
1164
+ className: `p-1.5 hover:bg-gray-200 rounded transition-colors ${showConversationList ? "bg-gray-200" : ""}`,
1165
+ "aria-label": "Conversation History",
1166
+ title: "Conversation History",
1167
+ children: /* @__PURE__ */ jsx(History, { size: 18, className: "text-gray-700" })
1168
+ }
1169
+ ),
1170
+ showMinimize && onToggleMinimize && /* @__PURE__ */ jsx(
1171
+ "button",
1172
+ {
1173
+ onClick: onToggleMinimize,
1174
+ className: "p-1 hover:bg-gray-200 rounded transition-colors",
1175
+ "aria-label": isMinimized ? "Expand" : "Minimize",
1176
+ children: isMinimized ? /* @__PURE__ */ jsx(ChevronUp, { size: 18, className: "text-gray-900" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 18, className: "text-gray-900" })
201
1177
  }
202
1178
  )
203
1179
  ] })
@@ -205,420 +1181,1399 @@ function WidgetHeader({
205
1181
  }
206
1182
  );
207
1183
  }
208
- function formatTime(date) {
209
- return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
1184
+ function StreamingText({
1185
+ content,
1186
+ isStreaming = false
1187
+ }) {
1188
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1189
+ /* @__PURE__ */ jsx(
1190
+ ReactMarkdown,
1191
+ {
1192
+ components: {
1193
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-bold", children }),
1194
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "list-disc pl-5 my-1", children }),
1195
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "list-decimal pl-5 my-1", children }),
1196
+ li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "[&>p]:inline [&>p]:m-0", children }),
1197
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-1 last:mb-0", children }),
1198
+ a: ({ href, children }) => /* @__PURE__ */ jsx(
1199
+ "a",
1200
+ {
1201
+ href,
1202
+ className: "underline hover:text-blue-300",
1203
+ target: "_blank",
1204
+ rel: "noopener noreferrer",
1205
+ children
1206
+ }
1207
+ ),
1208
+ code: ({ className, children, ...props }) => {
1209
+ const isInline = !className;
1210
+ return isInline ? /* @__PURE__ */ jsx(
1211
+ "code",
1212
+ {
1213
+ className: "bg-gray-100 px-1 py-0.5 rounded text-sm",
1214
+ ...props,
1215
+ children
1216
+ }
1217
+ ) : /* @__PURE__ */ jsx("code", { className, ...props, children });
1218
+ },
1219
+ pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: "bg-gray-100 p-2 rounded my-1 overflow-x-auto text-sm", children })
1220
+ },
1221
+ children: content
1222
+ }
1223
+ ),
1224
+ isStreaming && /* @__PURE__ */ jsx(
1225
+ "span",
1226
+ {
1227
+ className: "inline-block w-0.5 h-4 ml-0.5 align-text-bottom",
1228
+ style: {
1229
+ backgroundColor: "currentColor",
1230
+ animation: "cursor-blink 1s ease-in-out infinite"
1231
+ },
1232
+ "aria-hidden": "true"
1233
+ }
1234
+ ),
1235
+ /* @__PURE__ */ jsx("style", { children: `
1236
+ @keyframes cursor-blink {
1237
+ 0%, 100% { opacity: 1; }
1238
+ 50% { opacity: 0; }
1239
+ }
1240
+ ` })
1241
+ ] });
1242
+ }
1243
+ function ThinkingIndicator() {
1244
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1245
+ /* @__PURE__ */ jsx(
1246
+ "span",
1247
+ {
1248
+ className: "inline-block font-medium",
1249
+ style: {
1250
+ background: "linear-gradient(90deg, rgba(255,255,255,0.4) 0%, rgba(255,255,255,1) 50%, rgba(255,255,255,0.4) 100%)",
1251
+ backgroundSize: "200% 100%",
1252
+ WebkitBackgroundClip: "text",
1253
+ backgroundClip: "text",
1254
+ WebkitTextFillColor: "transparent",
1255
+ animation: "shimmer 1.5s ease-in-out infinite"
1256
+ },
1257
+ children: "Thinking..."
1258
+ }
1259
+ ),
1260
+ /* @__PURE__ */ jsx("style", { children: `
1261
+ @keyframes shimmer {
1262
+ 0% { background-position: 100% 0; }
1263
+ 100% { background-position: -100% 0; }
1264
+ }
1265
+ ` })
1266
+ ] });
1267
+ }
1268
+ function LoadingHistory() {
1269
+ return /* @__PURE__ */ jsx(
1270
+ motion.div,
1271
+ {
1272
+ initial: { opacity: 0, y: 10 },
1273
+ animate: { opacity: 1, y: 0 },
1274
+ className: "flex justify-center items-center py-8",
1275
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
1276
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [0, 0.15, 0.3].map((delay, i) => /* @__PURE__ */ jsx(
1277
+ motion.div,
1278
+ {
1279
+ className: "w-2 h-2 bg-gray-400 rounded-full",
1280
+ animate: { y: [0, -8, 0] },
1281
+ transition: { duration: 0.6, repeat: Infinity, delay }
1282
+ },
1283
+ i
1284
+ )) }),
1285
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500", children: "Loading conversation..." })
1286
+ ] })
1287
+ }
1288
+ );
1289
+ }
1290
+ var THINKING_MESSAGES = [
1291
+ "Pondering",
1292
+ "Analyzing",
1293
+ "Reasoning",
1294
+ "Considering",
1295
+ "Processing",
1296
+ "Evaluating",
1297
+ "Thinking",
1298
+ "Exploring",
1299
+ "Connecting",
1300
+ "Synthesizing"
1301
+ ];
1302
+ function getRandomThinkingMessage() {
1303
+ const idx = Math.floor(Math.random() * THINKING_MESSAGES.length);
1304
+ return THINKING_MESSAGES[idx] + "...";
1305
+ }
1306
+ function ShimmeringContent({ children }) {
1307
+ return /* @__PURE__ */ jsxs(
1308
+ "span",
1309
+ {
1310
+ className: "inline-block",
1311
+ style: {
1312
+ background: "linear-gradient(90deg, rgba(100,100,100,0.6) 0%, rgba(100,100,100,1) 50%, rgba(100,100,100,0.6) 100%)",
1313
+ backgroundSize: "200% 100%",
1314
+ WebkitBackgroundClip: "text",
1315
+ backgroundClip: "text",
1316
+ WebkitTextFillColor: "transparent",
1317
+ animation: "shimmer 1.5s ease-in-out infinite"
1318
+ },
1319
+ children: [
1320
+ children,
1321
+ /* @__PURE__ */ jsx("style", { children: `
1322
+ @keyframes shimmer {
1323
+ 0% { background-position: 100% 0; }
1324
+ 100% { background-position: -100% 0; }
1325
+ }
1326
+ ` })
1327
+ ]
1328
+ }
1329
+ );
1330
+ }
1331
+ function ReasoningTrace({ thinking, isComplete, toolCalls = [], isWaiting = false }) {
1332
+ const hasThinking = !!thinking && thinking.trim().length > 0;
1333
+ const hasToolCalls = toolCalls.length > 0;
1334
+ if (!isWaiting && !hasThinking && !hasToolCalls) return null;
1335
+ return /* @__PURE__ */ jsx("div", { className: "flex justify-start mb-2", children: /* @__PURE__ */ jsxs("div", { className: "max-w-[90%] space-y-1.5", children: [
1336
+ isWaiting && !hasThinking && /* @__PURE__ */ jsx(WaitingIndicator, {}),
1337
+ hasThinking && /* @__PURE__ */ jsx(ThinkingBlock, { thinking, isComplete }),
1338
+ toolCalls.map((tool) => /* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tool }, tool.id))
1339
+ ] }) });
1340
+ }
1341
+ function WaitingIndicator() {
1342
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-gray-500", children: [
1343
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-4 h-4", children: /* @__PURE__ */ jsx(ShimmeringContent, { children: /* @__PURE__ */ jsx(Brain, { className: "w-3.5 h-3.5" }) }) }),
1344
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: /* @__PURE__ */ jsx(ShimmeringContent, { children: getRandomThinkingMessage() }) })
1345
+ ] });
1346
+ }
1347
+ function ThinkingBlock({ thinking, isComplete }) {
1348
+ const [isExpanded, setIsExpanded] = useState(!isComplete);
1349
+ useLayoutEffect(() => {
1350
+ setIsExpanded(!isComplete);
1351
+ }, [isComplete]);
1352
+ const isInProgress = !isComplete;
1353
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-xs", children: [
1354
+ /* @__PURE__ */ jsxs(
1355
+ "button",
1356
+ {
1357
+ onClick: () => setIsExpanded(!isExpanded),
1358
+ className: `flex items-center gap-1.5 select-none transition-colors ${isInProgress ? "text-gray-500" : "text-gray-600 hover:text-gray-800"} cursor-pointer`,
1359
+ children: [
1360
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-4 h-4", children: isInProgress ? /* @__PURE__ */ jsx(ShimmeringContent, { children: /* @__PURE__ */ jsx(Brain, { className: "w-3.5 h-3.5" }) }) : /* @__PURE__ */ jsx(Brain, { className: "w-3.5 h-3.5" }) }),
1361
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: isInProgress ? /* @__PURE__ */ jsx(ShimmeringContent, { children: getRandomThinkingMessage() }) : "Thought" }),
1362
+ /* @__PURE__ */ jsx(
1363
+ motion.div,
1364
+ {
1365
+ animate: { rotate: isExpanded ? 90 : 0 },
1366
+ transition: { duration: 0.15 },
1367
+ className: "flex-shrink-0",
1368
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3" })
1369
+ }
1370
+ ),
1371
+ isComplete && /* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-green-500" })
1372
+ ]
1373
+ }
1374
+ ),
1375
+ /* @__PURE__ */ jsx(AnimatePresence, { children: isExpanded && /* @__PURE__ */ jsx(
1376
+ motion.div,
1377
+ {
1378
+ initial: { height: 0, opacity: 0 },
1379
+ animate: { height: "auto", opacity: 1 },
1380
+ exit: { height: 0, opacity: 0 },
1381
+ transition: { duration: 0.15 },
1382
+ className: "overflow-hidden",
1383
+ children: /* @__PURE__ */ jsx("div", { className: "border-l-2 border-gray-200 pl-3 ml-2", children: /* @__PURE__ */ jsx(
1384
+ "div",
1385
+ {
1386
+ className: `text-xs leading-relaxed whitespace-pre-wrap ${isComplete ? "text-gray-500" : "text-gray-600"}`,
1387
+ children: thinking
1388
+ }
1389
+ ) })
1390
+ }
1391
+ ) })
1392
+ ] });
210
1393
  }
211
- function MessageBubble({ message, isStreaming = false }) {
212
- const isUser = message.role === "user";
213
- const isWaiting = !message.content && !isUser;
1394
+ function ToolCallBlock({ toolCall }) {
1395
+ const [expanded, setExpanded] = useState(false);
1396
+ const hasArgs = Object.keys(toolCall.arguments || {}).length > 0;
1397
+ const isExecuting = toolCall.status === "executing";
1398
+ const isComplete = toolCall.status === "complete";
1399
+ const isError = toolCall.status === "error";
1400
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-xs", children: [
1401
+ /* @__PURE__ */ jsxs(
1402
+ "button",
1403
+ {
1404
+ onClick: () => hasArgs && setExpanded(!expanded),
1405
+ className: `flex items-center gap-1.5 select-none transition-colors ${hasArgs ? "cursor-pointer hover:text-gray-800" : "cursor-default"} ${isExecuting ? "text-gray-500" : "text-gray-600"}`,
1406
+ disabled: !hasArgs,
1407
+ children: [
1408
+ hasArgs && /* @__PURE__ */ jsx(
1409
+ motion.div,
1410
+ {
1411
+ animate: { rotate: expanded ? 90 : 0 },
1412
+ transition: { duration: 0.15 },
1413
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3" })
1414
+ }
1415
+ ),
1416
+ isExecuting && /* @__PURE__ */ jsx(Loader2, { className: "w-3 h-3 animate-spin text-blue-500" }),
1417
+ isComplete && /* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-green-500" }),
1418
+ isError && /* @__PURE__ */ jsx("span", { className: "w-3 h-3 text-red-500", children: "\u2715" }),
1419
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: toolCall.name }),
1420
+ /* @__PURE__ */ jsxs("span", { className: "text-gray-400", children: [
1421
+ isExecuting && "running...",
1422
+ isComplete && "done",
1423
+ isError && "failed"
1424
+ ] })
1425
+ ]
1426
+ }
1427
+ ),
1428
+ /* @__PURE__ */ jsx(AnimatePresence, { children: expanded && hasArgs && /* @__PURE__ */ jsx(
1429
+ motion.div,
1430
+ {
1431
+ initial: { height: 0, opacity: 0 },
1432
+ animate: { height: "auto", opacity: 1 },
1433
+ exit: { height: 0, opacity: 0 },
1434
+ transition: { duration: 0.15 },
1435
+ className: "overflow-hidden",
1436
+ children: /* @__PURE__ */ jsx("pre", { className: "mt-1 p-2 bg-gray-100 rounded text-[10px] font-mono text-gray-700 overflow-x-auto max-h-32 overflow-y-auto ml-4", children: JSON.stringify(toolCall.arguments, null, 2) })
1437
+ }
1438
+ ) })
1439
+ ] });
1440
+ }
1441
+ function MessageBubble({
1442
+ message,
1443
+ toolCalls = [],
1444
+ isLoading = false
1445
+ }) {
1446
+ const styles2 = useWidgetStyles2();
1447
+ const isWaiting = message.content === "Thinking..." || message.isBot && isLoading && !message.content;
1448
+ const hasThinking = message.isBot && message.thinking;
1449
+ const hasContent = message.content && message.content !== "Thinking...";
1450
+ const hasToolCalls = toolCalls.length > 0;
1451
+ const showReasoningTrace = message.isBot && (isWaiting || hasThinking || hasToolCalls);
1452
+ const isStreaming = Boolean(message.isBot && isLoading && hasContent);
214
1453
  return /* @__PURE__ */ jsxs(
215
1454
  "div",
216
1455
  {
217
- className: clsx(
218
- "crow-flex crow-flex-col",
219
- isUser ? "crow-items-end" : "crow-items-start"
220
- ),
1456
+ id: message.id,
1457
+ className: `flex flex-col ${message.isBot ? "items-start" : "items-end"}`,
221
1458
  children: [
222
- message.thinking && /* @__PURE__ */ jsx("div", { className: "crow-mb-2 crow-text-xs crow-text-gray-500 crow-bg-gray-100 crow-rounded-lg crow-px-3 crow-py-2 crow-max-w-[80%]", children: /* @__PURE__ */ jsxs("details", { open: !message.thinkingComplete, children: [
223
- /* @__PURE__ */ jsx("summary", { className: "crow-cursor-pointer crow-font-medium", children: message.thinkingComplete ? "Reasoning" : "Thinking..." }),
224
- /* @__PURE__ */ jsx("div", { className: "crow-mt-1 crow-text-gray-600 crow-whitespace-pre-wrap", children: message.thinking })
225
- ] }) }),
226
- isWaiting && /* @__PURE__ */ jsxs("div", { className: "crow-flex crow-items-center crow-gap-1 crow-text-gray-500 crow-text-sm crow-px-3 crow-py-2", children: [
227
- /* @__PURE__ */ jsx("span", { className: "crow-animate-pulse", children: "\u25CF" }),
228
- /* @__PURE__ */ jsx("span", { className: "crow-animate-pulse crow-animation-delay-100", children: "\u25CF" }),
229
- /* @__PURE__ */ jsx("span", { className: "crow-animate-pulse crow-animation-delay-200", children: "\u25CF" })
230
- ] }),
231
- message.content && /* @__PURE__ */ jsxs(
1459
+ showReasoningTrace && /* @__PURE__ */ jsx(
1460
+ ReasoningTrace,
1461
+ {
1462
+ thinking: message.thinking,
1463
+ isComplete: message.thinkingComplete,
1464
+ toolCalls,
1465
+ isWaiting: isWaiting && !hasThinking
1466
+ }
1467
+ ),
1468
+ hasContent && /* @__PURE__ */ jsxs(
232
1469
  "div",
233
1470
  {
234
- className: clsx(
235
- "crow-max-w-[80%] crow-rounded-2xl crow-px-4 crow-py-2 crow-transition-all",
236
- isUser ? "crow-bg-crow-primary crow-text-white" : "crow-bg-gray-100 crow-text-gray-900"
237
- ),
1471
+ className: "max-w-[80%] rounded-2xl px-4 py-2 transition-all duration-150",
1472
+ style: message.isBot ? {
1473
+ background: styles2.colors.botBubble,
1474
+ color: styles2.colors.botText
1475
+ } : {
1476
+ background: styles2.colors.userBubble,
1477
+ color: styles2.colors.userText,
1478
+ border: `1px solid ${styles2.colors.userBorder}`
1479
+ },
238
1480
  children: [
239
- /* @__PURE__ */ jsxs("div", { className: "crow-text-sm crow-whitespace-pre-wrap", children: [
240
- message.content,
241
- isStreaming && /* @__PURE__ */ jsx("span", { className: "crow-inline-block crow-w-1 crow-h-4 crow-ml-0.5 crow-bg-current crow-animate-pulse" })
242
- ] }),
243
- /* @__PURE__ */ jsx("div", { className: "crow-text-xs crow-opacity-60 crow-mt-1", children: formatTime(message.timestamp) })
1481
+ /* @__PURE__ */ jsx("div", { className: "text-sm whitespace-pre-wrap", children: message.isBot ? /* @__PURE__ */ jsx(
1482
+ StreamingText,
1483
+ {
1484
+ content: message.content,
1485
+ isStreaming
1486
+ }
1487
+ ) : /* @__PURE__ */ jsx(
1488
+ ReactMarkdown,
1489
+ {
1490
+ components: {
1491
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-bold", children }),
1492
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "list-disc pl-5 my-1", children }),
1493
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "list-decimal pl-5 my-1", children }),
1494
+ li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "[&>p]:inline [&>p]:m-0", children }),
1495
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-1 last:mb-0", children }),
1496
+ a: ({ href, children }) => /* @__PURE__ */ jsx(
1497
+ "a",
1498
+ {
1499
+ href,
1500
+ className: "underline hover:text-blue-300",
1501
+ target: "_blank",
1502
+ rel: "noopener noreferrer",
1503
+ children
1504
+ }
1505
+ )
1506
+ },
1507
+ children: message.content
1508
+ }
1509
+ ) }),
1510
+ /* @__PURE__ */ jsx("div", { className: "text-xs opacity-60 mt-1", children: formatTime(message.timestamp) })
244
1511
  ]
245
1512
  }
246
- ),
247
- message.citations && message.citations.length > 0 && /* @__PURE__ */ jsx("div", { className: "crow-mt-1 crow-flex crow-flex-wrap crow-gap-1", children: message.citations.map((citation, idx) => /* @__PURE__ */ jsx(
248
- "span",
1513
+ )
1514
+ ]
1515
+ }
1516
+ );
1517
+ }
1518
+ function MessageList({
1519
+ messages,
1520
+ activeToolCalls,
1521
+ isLoadingHistory,
1522
+ isGenerating = false
1523
+ }) {
1524
+ if (isLoadingHistory) {
1525
+ return /* @__PURE__ */ jsx(LoadingHistory, {});
1526
+ }
1527
+ const lastBotIndex = messages.reduce(
1528
+ (lastIdx, m, i) => m.isBot ? i : lastIdx,
1529
+ -1
1530
+ );
1531
+ return /* @__PURE__ */ jsx(Fragment, { children: messages.map((msg, index) => /* @__PURE__ */ jsx(
1532
+ MessageBubble,
1533
+ {
1534
+ message: msg,
1535
+ toolCalls: index === lastBotIndex ? activeToolCalls : void 0,
1536
+ isLoading: index === lastBotIndex && isGenerating
1537
+ },
1538
+ msg.id
1539
+ )) });
1540
+ }
1541
+ var MessagesContainer = forwardRef(
1542
+ ({ children, maxHeight }, ref) => {
1543
+ const styles2 = useWidgetStyles2();
1544
+ const internalRef = useRef(null);
1545
+ const lastScrollHeightRef = useRef(0);
1546
+ const isUserScrollingRef = useRef(false);
1547
+ const containerRef = ref || internalRef;
1548
+ const isNearBottom = useCallback(() => {
1549
+ const container = containerRef.current;
1550
+ if (!container) return true;
1551
+ const threshold = 100;
1552
+ return container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
1553
+ }, [containerRef]);
1554
+ const scrollToBottom = useCallback(() => {
1555
+ const container = containerRef.current;
1556
+ if (!container) return;
1557
+ container.scrollTo({
1558
+ top: container.scrollHeight,
1559
+ behavior: "smooth"
1560
+ });
1561
+ }, [containerRef]);
1562
+ useEffect(() => {
1563
+ const container = containerRef.current;
1564
+ if (!container) return;
1565
+ const handleScroll = () => {
1566
+ isUserScrollingRef.current = !isNearBottom();
1567
+ };
1568
+ container.addEventListener("scroll", handleScroll, { passive: true });
1569
+ return () => container.removeEventListener("scroll", handleScroll);
1570
+ }, [containerRef, isNearBottom]);
1571
+ useEffect(() => {
1572
+ const container = containerRef.current;
1573
+ if (!container) return;
1574
+ const currentHeight = container.scrollHeight;
1575
+ const heightChanged = currentHeight !== lastScrollHeightRef.current;
1576
+ if (heightChanged) {
1577
+ lastScrollHeightRef.current = currentHeight;
1578
+ if (!isUserScrollingRef.current || isNearBottom()) {
1579
+ scrollToBottom();
1580
+ }
1581
+ }
1582
+ });
1583
+ return /* @__PURE__ */ jsx(
1584
+ motion.div,
1585
+ {
1586
+ ref: containerRef,
1587
+ id: MESSAGES_CONTAINER_ID,
1588
+ initial: { opacity: 0 },
1589
+ animate: { opacity: 1 },
1590
+ exit: { opacity: 0 },
1591
+ transition: { duration: styles2.animations.duration },
1592
+ className: "relative flex-1 min-h-0 rounded-2xl mb-3 overflow-y-auto p-4 space-y-3 pointer-events-auto",
1593
+ style: {
1594
+ background: styles2.colors.messagesBackground,
1595
+ ...maxHeight && { maxHeight }
1596
+ },
1597
+ children
1598
+ }
1599
+ );
1600
+ }
1601
+ );
1602
+ MessagesContainer.displayName = "MessagesContainer";
1603
+ function ConversationList({
1604
+ conversations,
1605
+ currentConversationId,
1606
+ onSelect,
1607
+ onClose
1608
+ }) {
1609
+ return /* @__PURE__ */ jsxs(
1610
+ motion.div,
1611
+ {
1612
+ initial: { opacity: 0, height: 0 },
1613
+ animate: { opacity: 1, height: "auto" },
1614
+ exit: { opacity: 0, height: 0 },
1615
+ className: "mb-3 rounded-xl bg-gray-50 border border-gray-200",
1616
+ children: [
1617
+ /* @__PURE__ */ jsxs("div", { className: "p-2 border-b border-gray-200 flex justify-between items-center", children: [
1618
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700", children: "Recent Conversations" }),
1619
+ /* @__PURE__ */ jsx("button", { onClick: onClose, className: "p-1 hover:bg-gray-200 rounded", children: /* @__PURE__ */ jsx(X, { size: 14, className: "text-gray-500" }) })
1620
+ ] }),
1621
+ /* @__PURE__ */ jsx("div", { className: "max-h-[200px] overflow-y-auto", children: conversations.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: "No conversations yet" }) : conversations.map((conv) => /* @__PURE__ */ jsxs(
1622
+ "button",
249
1623
  {
250
- className: "crow-text-xs crow-bg-blue-100 crow-text-blue-700 crow-px-2 crow-py-0.5 crow-rounded",
251
- children: citation.filename
1624
+ onClick: () => onSelect(conv.id),
1625
+ className: `w-full p-3 text-left hover:bg-gray-100 transition-colors border-b border-gray-100 last:border-0 ${currentConversationId === conv.id ? "bg-blue-50" : ""}`,
1626
+ children: [
1627
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-800 truncate", children: conv.name || "Untitled conversation" }),
1628
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mt-0.5", children: formatConversationDate(conv.updated_at) })
1629
+ ]
252
1630
  },
253
- idx
1631
+ conv.id
254
1632
  )) })
255
1633
  ]
256
1634
  }
257
1635
  );
258
1636
  }
259
- function MessageList({ messages, isLoading = false, className }) {
260
- const containerRef = useRef(null);
261
- useEffect(() => {
262
- if (containerRef.current) {
263
- containerRef.current.scrollTop = containerRef.current.scrollHeight;
264
- }
265
- }, [messages]);
1637
+ function WorkflowPanel({ workflow, onExit }) {
266
1638
  return /* @__PURE__ */ jsx(
267
- "div",
1639
+ motion.div,
268
1640
  {
269
- ref: containerRef,
270
- className: `crow-flex-1 crow-overflow-y-auto crow-px-4 crow-py-4 crow-space-y-3 ${className || ""}`,
271
- children: messages.map((message, index) => {
272
- const isLastBotMessage = message.role === "assistant" && index === messages.length - 1;
273
- return /* @__PURE__ */ jsx(
274
- MessageBubble,
1641
+ initial: { opacity: 0, y: -10 },
1642
+ animate: { opacity: 1, y: 0 },
1643
+ exit: { opacity: 0, y: -10 },
1644
+ transition: { duration: 0.3 },
1645
+ className: "mb-3 p-3 rounded-xl bg-gray-50 border border-gray-200",
1646
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1647
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900 flex items-center gap-1", children: workflow.name }),
1648
+ /* @__PURE__ */ jsx(
1649
+ "button",
275
1650
  {
276
- message,
277
- isStreaming: isLastBotMessage && isLoading
1651
+ onClick: onExit,
1652
+ className: "text-xs text-gray-500 hover:text-gray-700 underline",
1653
+ children: "Exit"
1654
+ }
1655
+ )
1656
+ ] })
1657
+ }
1658
+ );
1659
+ }
1660
+ function PoweredByBadge({ apiUrl = "" }) {
1661
+ const styles2 = useWidgetStyles2();
1662
+ const branding = styles2.branding;
1663
+ if (!branding.showPoweredBy) return null;
1664
+ const logoUrl = branding.logoUrl?.startsWith("http") ? branding.logoUrl : `${apiUrl}${branding.logoUrl}`;
1665
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1.5 mb-3 text-[11px] text-gray-400", children: [
1666
+ branding.showLogo && logoUrl && /* @__PURE__ */ jsx(
1667
+ "img",
1668
+ {
1669
+ src: logoUrl,
1670
+ alt: "Crow",
1671
+ width: 14,
1672
+ height: 14,
1673
+ className: "opacity-50"
1674
+ }
1675
+ ),
1676
+ /* @__PURE__ */ jsx("span", { children: branding.poweredByText })
1677
+ ] });
1678
+ }
1679
+ var ModelSelector = ({
1680
+ models,
1681
+ selectedModel,
1682
+ onModelChange,
1683
+ disabled = false
1684
+ }) => {
1685
+ const [isOpen, setIsOpen] = useState(false);
1686
+ const dropdownRef = useRef(null);
1687
+ useEffect(() => {
1688
+ const handleClickOutside = (event) => {
1689
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
1690
+ setIsOpen(false);
1691
+ }
1692
+ };
1693
+ document.addEventListener("mousedown", handleClickOutside);
1694
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1695
+ }, []);
1696
+ const selectedModelData = models.find((m) => m.id === selectedModel);
1697
+ const groupedModels = models.reduce((acc, model) => {
1698
+ if (!acc[model.provider]) acc[model.provider] = [];
1699
+ acc[model.provider].push(model);
1700
+ return acc;
1701
+ }, {});
1702
+ return /* @__PURE__ */ jsxs("div", { className: "relative", ref: dropdownRef, children: [
1703
+ /* @__PURE__ */ jsxs(
1704
+ "button",
1705
+ {
1706
+ type: "button",
1707
+ onClick: () => !disabled && setIsOpen(!isOpen),
1708
+ disabled,
1709
+ className: cn(
1710
+ "flex items-center gap-1 px-2 py-1 rounded-md text-xs",
1711
+ "bg-transparent text-[#9CA3AF]",
1712
+ "hover:text-gray-700 transition-colors",
1713
+ disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
1714
+ ),
1715
+ children: [
1716
+ /* @__PURE__ */ jsx("span", { children: selectedModelData?.name || "Select Model" }),
1717
+ /* @__PURE__ */ jsx(
1718
+ ChevronDown,
1719
+ {
1720
+ className: cn(
1721
+ "w-3 h-3 transition-transform",
1722
+ isOpen ? "rotate-180" : ""
1723
+ )
1724
+ }
1725
+ )
1726
+ ]
1727
+ }
1728
+ ),
1729
+ isOpen && /* @__PURE__ */ jsx("div", { className: "absolute bottom-full mb-2 left-0 min-w-[180px] bg-white border border-gray-200 rounded-xl shadow-lg z-50 overflow-hidden", children: Object.entries(groupedModels).map(([provider, providerModels]) => /* @__PURE__ */ jsxs("div", { children: [
1730
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 text-xs font-medium text-gray-400 uppercase tracking-wide bg-gray-50", children: provider }),
1731
+ providerModels.map((model) => /* @__PURE__ */ jsxs(
1732
+ "button",
1733
+ {
1734
+ onClick: () => {
1735
+ onModelChange(model.id);
1736
+ setIsOpen(false);
278
1737
  },
279
- message.id
280
- );
281
- })
282
- }
283
- );
1738
+ className: cn(
1739
+ "w-full flex items-center justify-between px-3 py-1.5 text-sm",
1740
+ "hover:bg-gray-100 transition-colors",
1741
+ model.id === selectedModel ? "text-gray-900" : "text-gray-600"
1742
+ ),
1743
+ children: [
1744
+ /* @__PURE__ */ jsx("span", { children: model.name }),
1745
+ model.id === selectedModel && /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 text-green-500" })
1746
+ ]
1747
+ },
1748
+ model.id
1749
+ ))
1750
+ ] }, provider)) })
1751
+ ] });
1752
+ };
1753
+ var styles = `
1754
+ *:focus-visible {
1755
+ outline-offset: 0 !important;
1756
+ --ring-offset: 0 !important;
1757
+ }
1758
+ textarea::-webkit-scrollbar {
1759
+ width: 6px;
1760
+ }
1761
+ textarea::-webkit-scrollbar-track {
1762
+ background: transparent;
1763
+ }
1764
+ textarea::-webkit-scrollbar-thumb {
1765
+ background-color: #444444;
1766
+ border-radius: 3px;
1767
+ }
1768
+ textarea::-webkit-scrollbar-thumb:hover {
1769
+ background-color: #555555;
1770
+ }
1771
+ `;
1772
+ if (typeof document !== "undefined") {
1773
+ const styleSheet = document.createElement("style");
1774
+ styleSheet.innerText = styles;
1775
+ document.head.appendChild(styleSheet);
284
1776
  }
285
- function PromptInput({
286
- onSend,
287
- onStop,
288
- isLoading = false,
289
- placeholder = "Type your message...",
290
- disabled = false,
291
- className
292
- }) {
293
- const [value, setValue] = useState("");
294
- const textareaRef = useRef(null);
295
- useEffect(() => {
296
- const textarea = textareaRef.current;
297
- if (textarea) {
298
- textarea.style.height = "auto";
299
- textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
300
- }
301
- }, [value]);
302
- const handleSubmit = useCallback(() => {
303
- if (value.trim() && !isLoading) {
304
- onSend(value.trim());
305
- setValue("");
306
- }
307
- }, [value, isLoading, onSend]);
308
- const handleKeyDown = useCallback(
309
- (e) => {
310
- if (e.key === "Enter" && !e.shiftKey) {
311
- e.preventDefault();
312
- handleSubmit();
313
- }
314
- },
315
- [handleSubmit]
316
- );
317
- const hasContent = value.trim().length > 0;
318
- return /* @__PURE__ */ jsxs(
319
- "div",
1777
+ var Textarea = React3.forwardRef(
1778
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1779
+ "textarea",
320
1780
  {
321
- className: clsx(
322
- "crow-flex crow-items-end crow-gap-2 crow-p-3 crow-border-t crow-border-gray-200 crow-bg-white",
1781
+ className: cn(
1782
+ "flex w-full rounded-md border-none bg-transparent px-3 py-1.5 text-base text-gray-900 placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-0 disabled:cursor-not-allowed disabled:opacity-50 min-h-[32px] resize-none",
323
1783
  className
324
1784
  ),
325
- children: [
326
- /* @__PURE__ */ jsx(
327
- "textarea",
328
- {
329
- ref: textareaRef,
330
- value,
331
- onChange: (e) => setValue(e.target.value),
332
- onKeyDown: handleKeyDown,
333
- placeholder,
334
- disabled: disabled || isLoading,
335
- rows: 1,
336
- className: clsx(
337
- "crow-flex-1 crow-resize-none crow-border crow-border-gray-300 crow-rounded-xl crow-px-4 crow-py-2",
338
- "crow-text-sm crow-text-gray-900 crow-placeholder-gray-500",
339
- "focus:crow-outline-none focus:crow-ring-2 focus:crow-ring-crow-primary focus:crow-border-transparent",
340
- "disabled:crow-bg-gray-50 disabled:crow-cursor-not-allowed"
341
- )
342
- }
1785
+ ref,
1786
+ rows: 1,
1787
+ ...props
1788
+ }
1789
+ )
1790
+ );
1791
+ Textarea.displayName = "Textarea";
1792
+ var TooltipProvider = TooltipPrimitive.Provider;
1793
+ var Tooltip = TooltipPrimitive.Root;
1794
+ var TooltipTrigger = TooltipPrimitive.Trigger;
1795
+ var TooltipContent = React3.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
1796
+ TooltipPrimitive.Content,
1797
+ {
1798
+ ref,
1799
+ sideOffset,
1800
+ className: cn(
1801
+ "z-50 overflow-hidden rounded-md border border-[#333333] bg-[#1F2023] px-3 py-1.5 text-sm text-white shadow-md animate-in fade-in-0 zoom-in-95",
1802
+ className
1803
+ ),
1804
+ ...props
1805
+ }
1806
+ ));
1807
+ TooltipContent.displayName = "TooltipContent";
1808
+ var Button = React3.forwardRef(
1809
+ ({ className, variant = "default", size = "default", ...props }, ref) => {
1810
+ const variantClasses = {
1811
+ default: "bg-white hover:bg-white/80 text-black",
1812
+ outline: "border border-[#444444] bg-transparent hover:bg-[#3A3A40]",
1813
+ ghost: "bg-transparent hover:bg-[#3A3A40]"
1814
+ };
1815
+ const sizeClasses = {
1816
+ default: "h-10 px-4 py-2",
1817
+ sm: "h-8 px-3 text-sm",
1818
+ lg: "h-12 px-6",
1819
+ icon: "h-8 w-8 rounded-full aspect-[1/1]"
1820
+ };
1821
+ return /* @__PURE__ */ jsx(
1822
+ "button",
1823
+ {
1824
+ className: cn(
1825
+ "inline-flex items-center justify-center font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50",
1826
+ variantClasses[variant],
1827
+ sizeClasses[size],
1828
+ className
343
1829
  ),
344
- /* @__PURE__ */ jsx(
345
- "button",
1830
+ ref,
1831
+ ...props
1832
+ }
1833
+ );
1834
+ }
1835
+ );
1836
+ Button.displayName = "Button";
1837
+ var PromptInputContext = React3.createContext({
1838
+ isLoading: false,
1839
+ value: "",
1840
+ setValue: () => {
1841
+ },
1842
+ maxHeight: 240,
1843
+ onSubmit: void 0,
1844
+ disabled: false
1845
+ });
1846
+ function usePromptInput() {
1847
+ const context = React3.useContext(PromptInputContext);
1848
+ if (!context) {
1849
+ throw new Error("usePromptInput must be used within a PromptInput");
1850
+ }
1851
+ return context;
1852
+ }
1853
+ var PromptInput = React3.forwardRef(
1854
+ ({
1855
+ className,
1856
+ isLoading = false,
1857
+ maxHeight = 240,
1858
+ value,
1859
+ onValueChange,
1860
+ onSubmit,
1861
+ children,
1862
+ disabled = false
1863
+ }, ref) => {
1864
+ const [internalValue, setInternalValue] = React3.useState(value || "");
1865
+ const handleChange = (newValue) => {
1866
+ setInternalValue(newValue);
1867
+ onValueChange?.(newValue);
1868
+ };
1869
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(
1870
+ PromptInputContext.Provider,
1871
+ {
1872
+ value: {
1873
+ isLoading,
1874
+ value: value ?? internalValue,
1875
+ setValue: onValueChange ?? handleChange,
1876
+ maxHeight,
1877
+ onSubmit,
1878
+ disabled
1879
+ },
1880
+ children: /* @__PURE__ */ jsx(
1881
+ "div",
346
1882
  {
347
- onClick: isLoading ? onStop : handleSubmit,
348
- disabled: !isLoading && !hasContent,
349
- className: clsx(
350
- "crow-flex-shrink-0 crow-w-10 crow-h-10 crow-rounded-full crow-flex crow-items-center crow-justify-center",
351
- "crow-transition-all crow-duration-200",
352
- isLoading ? "crow-bg-red-500 hover:crow-bg-red-600 crow-text-white" : hasContent ? "crow-bg-crow-primary hover:crow-bg-crow-primary-dark crow-text-white" : "crow-bg-gray-200 crow-text-gray-400 crow-cursor-not-allowed"
1883
+ ref,
1884
+ className: cn(
1885
+ "rounded-3xl border border-[#444444] bg-[#1F2023] p-1.5 shadow-[0_8px_30px_rgba(0,0,0,0.24)] transition-all duration-300",
1886
+ isLoading && "border-red-500/70",
1887
+ className
353
1888
  ),
354
- "aria-label": isLoading ? "Stop generation" : "Send message",
355
- children: isLoading ? /* @__PURE__ */ jsx("svg", { className: "crow-w-4 crow-h-4", fill: "currentColor", viewBox: "0 0 16 16", children: /* @__PURE__ */ jsx("rect", { width: "10", height: "10", x: "3", y: "3", rx: "1" }) }) : /* @__PURE__ */ jsx("svg", { className: "crow-w-4 crow-h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 12h14M12 5l7 7-7 7" }) })
1889
+ children
356
1890
  }
357
1891
  )
358
- ]
1892
+ }
1893
+ ) });
1894
+ }
1895
+ );
1896
+ PromptInput.displayName = "PromptInput";
1897
+ var PromptInputTextarea = ({ className, onKeyDown, disableAutosize = false, placeholder, ...props }) => {
1898
+ const { value, setValue, maxHeight, onSubmit, disabled } = usePromptInput();
1899
+ const textareaRef = React3.useRef(null);
1900
+ React3.useEffect(() => {
1901
+ if (disableAutosize || !textareaRef.current) return;
1902
+ textareaRef.current.style.height = "auto";
1903
+ textareaRef.current.style.height = typeof maxHeight === "number" ? `${Math.min(textareaRef.current.scrollHeight, maxHeight)}px` : `min(${textareaRef.current.scrollHeight}px, ${maxHeight})`;
1904
+ }, [value, maxHeight, disableAutosize]);
1905
+ const handleKeyDown = (e) => {
1906
+ if (e.key === "Enter" && !e.shiftKey) {
1907
+ e.preventDefault();
1908
+ onSubmit?.();
1909
+ }
1910
+ onKeyDown?.(e);
1911
+ };
1912
+ return /* @__PURE__ */ jsx(
1913
+ Textarea,
1914
+ {
1915
+ ref: textareaRef,
1916
+ value,
1917
+ onChange: (e) => setValue(e.target.value),
1918
+ onKeyDown: handleKeyDown,
1919
+ className: cn("text-base", className),
1920
+ disabled,
1921
+ placeholder,
1922
+ ...props
359
1923
  }
360
1924
  );
361
- }
1925
+ };
1926
+ var PromptInputActions = ({
1927
+ children,
1928
+ className,
1929
+ ...props
1930
+ }) => /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2", className), ...props, children });
1931
+ var PromptInputAction = ({
1932
+ tooltip,
1933
+ children,
1934
+ side = "top",
1935
+ ...props
1936
+ }) => {
1937
+ return /* @__PURE__ */ jsxs(Tooltip, { ...props, children: [
1938
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
1939
+ /* @__PURE__ */ jsx(TooltipContent, { side, children: tooltip })
1940
+ ] });
1941
+ };
1942
+ var PromptInputBox = React3.forwardRef(
1943
+ ({
1944
+ onSend = () => {
1945
+ },
1946
+ onStop,
1947
+ isLoading = false,
1948
+ placeholder = "Type your message here...",
1949
+ className,
1950
+ selectedModel = "gpt-4o",
1951
+ onModelChange,
1952
+ availableModels = []
1953
+ }, ref) => {
1954
+ const [input, setInput] = React3.useState("");
1955
+ const promptBoxRef = React3.useRef(null);
1956
+ const handleSubmit = () => {
1957
+ if (input.trim()) {
1958
+ onSend(input);
1959
+ setInput("");
1960
+ }
1961
+ };
1962
+ const hasContent = input.trim() !== "";
1963
+ return /* @__PURE__ */ jsxs(
1964
+ PromptInput,
1965
+ {
1966
+ value: input,
1967
+ onValueChange: setInput,
1968
+ isLoading,
1969
+ onSubmit: handleSubmit,
1970
+ className: cn(
1971
+ "w-full bg-[#1F2023] border-[#444444] shadow-[0_8px_30px_rgba(0,0,0,0.24)] transition-all duration-300 ease-in-out",
1972
+ className
1973
+ ),
1974
+ disabled: isLoading,
1975
+ ref: ref || promptBoxRef,
1976
+ children: [
1977
+ /* @__PURE__ */ jsx("div", { className: "transition-all duration-300 opacity-100", children: /* @__PURE__ */ jsx(PromptInputTextarea, { placeholder, className: "text-base" }) }),
1978
+ /* @__PURE__ */ jsxs(PromptInputActions, { className: "flex items-center justify-between gap-2 p-0 pt-1", children: [
1979
+ /* @__PURE__ */ jsx("div", { className: "flex items-center", children: availableModels.length > 0 && onModelChange && /* @__PURE__ */ jsx(
1980
+ ModelSelector,
1981
+ {
1982
+ models: availableModels,
1983
+ selectedModel,
1984
+ onModelChange,
1985
+ disabled: isLoading
1986
+ }
1987
+ ) }),
1988
+ /* @__PURE__ */ jsx(
1989
+ PromptInputAction,
1990
+ {
1991
+ tooltip: isLoading ? "Stop generation" : hasContent ? "Send message" : "Type a message",
1992
+ children: /* @__PURE__ */ jsx(
1993
+ Button,
1994
+ {
1995
+ variant: "default",
1996
+ size: "icon",
1997
+ className: cn(
1998
+ "h-7 w-7 rounded-full transition-all duration-200",
1999
+ isLoading ? "!bg-red-500 hover:!bg-red-600 !text-white" : hasContent ? "!bg-white hover:!bg-white/80 !text-[#1F2023]" : "!bg-transparent hover:!bg-gray-600/30 !text-[#1F2023] hover:!text-[#1F2023]"
2000
+ ),
2001
+ onClick: () => {
2002
+ if (isLoading && onStop) {
2003
+ onStop();
2004
+ } else if (hasContent) {
2005
+ handleSubmit();
2006
+ }
2007
+ },
2008
+ children: isLoading ? /* @__PURE__ */ jsx(Square, { className: "h-3.5 w-3.5 fill-white text-white" }) : /* @__PURE__ */ jsx(ArrowUp, { className: "h-3.5 w-3.5 text-[#1F2023]" })
2009
+ }
2010
+ )
2011
+ }
2012
+ )
2013
+ ] })
2014
+ ]
2015
+ }
2016
+ );
2017
+ }
2018
+ );
2019
+ PromptInputBox.displayName = "PromptInputBox";
362
2020
  function CrowWidget({
363
2021
  productId,
364
- apiUrl,
365
- model,
366
- agentName = "Assistant",
367
- defaultCollapsed = true,
368
- onIdentify,
369
- tools,
370
- className,
371
- zIndex = 999999
2022
+ apiUrl = "",
2023
+ variant = "floating",
2024
+ styles: propStyles,
2025
+ previewMode = false,
2026
+ onReady
372
2027
  }) {
373
- const contextClient = useCrowClientOptional();
374
- const standaloneClientRef = useRef(null);
375
- if (!contextClient && !standaloneClientRef.current) {
376
- standaloneClientRef.current = new CrowClient({ productId, apiUrl, model });
377
- }
378
- const client = contextClient || standaloneClientRef.current;
379
- useEffect(() => {
380
- return () => {
381
- if (standaloneClientRef.current) {
382
- standaloneClientRef.current.destroy();
2028
+ const { styles: styles2, isLoading: isLoadingStyles, agentName } = useWidgetStyles({
2029
+ productId,
2030
+ apiUrl,
2031
+ propStyles,
2032
+ skip: previewMode
2033
+ });
2034
+ const messagesContainerRef = useRef(null);
2035
+ const executeClientToolRef = useRef(null);
2036
+ const [isCollapsed, setIsCollapsed] = useState(variant === "floating");
2037
+ const [showConversationList, setShowConversationList] = useState(false);
2038
+ const [isVerifiedUser, setIsVerifiedUser] = useState(false);
2039
+ const {
2040
+ activeWorkflow,
2041
+ startWorkflow,
2042
+ updateTodo,
2043
+ markComplete,
2044
+ endWorkflow
2045
+ } = useWorkflow({
2046
+ productId,
2047
+ apiUrl,
2048
+ conversationId: null,
2049
+ selectedModel: DEFAULT_MODEL
2050
+ });
2051
+ const conversations = useConversations({ productId, apiUrl });
2052
+ const [shouldRestoreHistory, setShouldRestoreHistory] = useState(false);
2053
+ const hasRestoredHistoryRef = useRef(false);
2054
+ const chat = useChat({
2055
+ productId,
2056
+ apiUrl,
2057
+ onVerificationStatus: (isVerified) => {
2058
+ setIsVerifiedUser(isVerified);
2059
+ },
2060
+ onConversationId: () => {
2061
+ },
2062
+ onWorkflowEvent: (event) => {
2063
+ switch (event.type) {
2064
+ case "started":
2065
+ if (event.name && event.todos) {
2066
+ startWorkflow(event.name, event.todos);
2067
+ }
2068
+ break;
2069
+ case "todo_updated":
2070
+ if (event.todoId && event.todoStatus) {
2071
+ updateTodo(event.todoId, event.todoStatus);
2072
+ }
2073
+ break;
2074
+ case "ended":
2075
+ endWorkflow(2e3);
2076
+ break;
2077
+ case "complete_prompt":
2078
+ markComplete();
2079
+ break;
383
2080
  }
384
- };
385
- }, []);
386
- useEffect(() => {
387
- if (tools) {
388
- client.registerTools(tools);
2081
+ },
2082
+ onToolCall: (event) => {
2083
+ if (event.type === "client_call" && event.toolName) {
2084
+ console.log("[Crow Widget] Executing client tool:", event.toolName, event.arguments);
2085
+ const result = executeClientToolRef.current?.(event.toolName, event.arguments || {});
2086
+ result?.then((r) => console.log("[Crow Widget] Tool result:", r)).catch((e) => console.error("[Crow Widget] Tool error:", e));
2087
+ }
2088
+ },
2089
+ onRestoredConversation: () => {
2090
+ setShouldRestoreHistory(true);
389
2091
  }
390
- }, [client, tools]);
2092
+ });
391
2093
  useEffect(() => {
392
- if (onIdentify) {
393
- onIdentify((options) => client.identify(options));
2094
+ if (shouldRestoreHistory && chat.conversationId && !hasRestoredHistoryRef.current) {
2095
+ hasRestoredHistoryRef.current = true;
2096
+ console.log("[Crow Widget] Restoring conversation history:", chat.conversationId);
2097
+ conversations.loadAnonymousConversationHistory(chat.conversationId).then((historyMessages) => {
2098
+ if (historyMessages.length > 0) {
2099
+ chat.loadMessages(historyMessages);
2100
+ }
2101
+ });
2102
+ }
2103
+ }, [shouldRestoreHistory, chat.conversationId, conversations, chat]);
2104
+ const workflowActions = useWorkflow({
2105
+ productId,
2106
+ apiUrl,
2107
+ conversationId: chat.conversationId,
2108
+ selectedModel: chat.selectedModel,
2109
+ onMessage: (content) => {
2110
+ chat.loadMessages([
2111
+ ...chat.messages,
2112
+ {
2113
+ id: `bot-${Date.now()}`,
2114
+ content,
2115
+ isBot: true,
2116
+ timestamp: /* @__PURE__ */ new Date()
2117
+ }
2118
+ ]);
394
2119
  }
395
- }, [client, onIdentify]);
396
- const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
397
- const { messages, isLoading, sendMessage, stop, clearMessages } = useChat({
398
- client
399
2120
  });
400
- return /* @__PURE__ */ jsxs(Fragment, { children: [
401
- /* @__PURE__ */ jsx(ChatBubble, { isOpen: !isCollapsed, onClick: () => setIsCollapsed(!isCollapsed) }),
402
- !isCollapsed && /* @__PURE__ */ jsxs(
403
- "div",
404
- {
405
- className: clsx(
406
- "crow-fixed crow-flex crow-flex-col",
407
- "crow-bg-white crow-rounded-2xl crow-shadow-2xl",
408
- "crow-border crow-border-gray-200",
409
- "crow-overflow-hidden crow-animate-fade-in",
410
- className
411
- ),
412
- style: {
413
- zIndex,
414
- right: "20px",
415
- bottom: "90px",
416
- width: "380px",
417
- height: "550px",
418
- maxHeight: "calc(100vh - 120px)"
419
- },
420
- children: [
2121
+ const { executeClientTool } = useCrowAPI({
2122
+ onIdentified: async () => {
2123
+ setIsVerifiedUser(true);
2124
+ await conversations.loadConversations();
2125
+ },
2126
+ onReset: () => {
2127
+ setIsVerifiedUser(false);
2128
+ chat.resetMessages();
2129
+ }
2130
+ });
2131
+ executeClientToolRef.current = executeClientTool;
2132
+ useEffect(() => {
2133
+ if (!isLoadingStyles) {
2134
+ onReady?.();
2135
+ }
2136
+ }, [isLoadingStyles, onReady]);
2137
+ const handleSend = (message) => {
2138
+ if (!message.trim()) return;
2139
+ setIsCollapsed(false);
2140
+ chat.sendMessage(message);
2141
+ };
2142
+ const handleNewChat = () => {
2143
+ chat.resetMessages();
2144
+ setShowConversationList(false);
2145
+ };
2146
+ const handleToggleHistory = () => {
2147
+ setShowConversationList(!showConversationList);
2148
+ if (!showConversationList) {
2149
+ conversations.loadConversations();
2150
+ }
2151
+ };
2152
+ const handleSelectConversation = async (convId) => {
2153
+ const historyMessages = await conversations.loadConversationHistory(convId);
2154
+ if (historyMessages.length > 0) {
2155
+ chat.loadMessages(historyMessages);
2156
+ chat.setConversationId(convId);
2157
+ } else {
2158
+ chat.resetMessages();
2159
+ }
2160
+ setShowConversationList(false);
2161
+ };
2162
+ const handleCloseConversationList = () => {
2163
+ setShowConversationList(false);
2164
+ };
2165
+ const handleExitWorkflow = async () => {
2166
+ await workflowActions.exitWorkflow();
2167
+ };
2168
+ const handleBubbleClick = () => {
2169
+ setIsCollapsed(!isCollapsed);
2170
+ };
2171
+ return /* @__PURE__ */ jsxs(
2172
+ WidgetStyleProvider,
2173
+ {
2174
+ styles: styles2,
2175
+ agentName,
2176
+ isLoading: isLoadingStyles,
2177
+ variant,
2178
+ children: [
2179
+ variant === "floating" && /* @__PURE__ */ jsxs(Fragment, { children: [
2180
+ /* @__PURE__ */ jsx(ChatBubble, { isExpanded: !isCollapsed, onClick: handleBubbleClick }),
2181
+ !isCollapsed && /* @__PURE__ */ jsxs(WidgetShell, { children: [
2182
+ /* @__PURE__ */ jsx(
2183
+ WidgetHeader,
2184
+ {
2185
+ isVerifiedUser,
2186
+ showConversationList,
2187
+ onNewChat: handleNewChat,
2188
+ onToggleHistory: handleToggleHistory
2189
+ }
2190
+ ),
2191
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showConversationList && isVerifiedUser && /* @__PURE__ */ jsx(
2192
+ ConversationList,
2193
+ {
2194
+ conversations: conversations.conversations,
2195
+ currentConversationId: chat.conversationId,
2196
+ onSelect: handleSelectConversation,
2197
+ onClose: handleCloseConversationList
2198
+ }
2199
+ ) }),
2200
+ /* @__PURE__ */ jsx(AnimatePresence, { children: activeWorkflow && /* @__PURE__ */ jsx(
2201
+ WorkflowPanel,
2202
+ {
2203
+ workflow: activeWorkflow,
2204
+ onExit: handleExitWorkflow
2205
+ }
2206
+ ) }),
2207
+ /* @__PURE__ */ jsx(AnimatePresence, { children: (chat.messages.length > 0 || conversations.isLoadingHistory) && /* @__PURE__ */ jsx(MessagesContainer, { ref: messagesContainerRef, children: /* @__PURE__ */ jsx(
2208
+ MessageList,
2209
+ {
2210
+ messages: chat.messages,
2211
+ activeToolCalls: chat.activeToolCalls,
2212
+ isLoadingHistory: conversations.isLoadingHistory,
2213
+ isGenerating: chat.isLoading
2214
+ }
2215
+ ) }) }),
2216
+ /* @__PURE__ */ jsxs("div", { className: "mt-auto", children: [
2217
+ /* @__PURE__ */ jsx(PoweredByBadge, { apiUrl }),
2218
+ /* @__PURE__ */ jsx(
2219
+ PromptInputBox,
2220
+ {
2221
+ onSend: handleSend,
2222
+ onStop: chat.stopGeneration,
2223
+ placeholder: "Type your message...",
2224
+ isLoading: chat.isLoading,
2225
+ className: "!bg-white !border-gray-300 backdrop-blur-md"
2226
+ }
2227
+ )
2228
+ ] })
2229
+ ] })
2230
+ ] }),
2231
+ variant === "embedded" && /* @__PURE__ */ jsxs(WidgetShell, { children: [
421
2232
  /* @__PURE__ */ jsx(
422
2233
  WidgetHeader,
423
2234
  {
424
- title: agentName,
425
- onNewChat: clearMessages,
426
- showNewChat: true
2235
+ isVerifiedUser,
2236
+ showConversationList,
2237
+ onNewChat: handleNewChat,
2238
+ onToggleHistory: handleToggleHistory
427
2239
  }
428
2240
  ),
429
- /* @__PURE__ */ jsx(
430
- MessageList,
2241
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showConversationList && isVerifiedUser && /* @__PURE__ */ jsx(
2242
+ ConversationList,
431
2243
  {
432
- messages,
433
- isLoading,
434
- className: "crow-flex-1"
2244
+ conversations: conversations.conversations,
2245
+ currentConversationId: chat.conversationId,
2246
+ onSelect: handleSelectConversation,
2247
+ onClose: handleCloseConversationList
435
2248
  }
436
- ),
437
- /* @__PURE__ */ jsx(
438
- PromptInput,
2249
+ ) }),
2250
+ /* @__PURE__ */ jsx(AnimatePresence, { children: activeWorkflow && /* @__PURE__ */ jsx(
2251
+ WorkflowPanel,
439
2252
  {
440
- onSend: sendMessage,
441
- onStop: stop,
442
- isLoading,
443
- placeholder: "Type your message..."
2253
+ workflow: activeWorkflow,
2254
+ onExit: handleExitWorkflow
444
2255
  }
445
- )
446
- ]
447
- }
448
- )
449
- ] });
2256
+ ) }),
2257
+ /* @__PURE__ */ jsx(AnimatePresence, { children: (chat.messages.length > 0 || conversations.isLoadingHistory) && /* @__PURE__ */ jsx(MessagesContainer, { ref: messagesContainerRef, children: /* @__PURE__ */ jsx(
2258
+ MessageList,
2259
+ {
2260
+ messages: chat.messages,
2261
+ activeToolCalls: chat.activeToolCalls,
2262
+ isLoadingHistory: conversations.isLoadingHistory,
2263
+ isGenerating: chat.isLoading
2264
+ }
2265
+ ) }) }),
2266
+ /* @__PURE__ */ jsxs("div", { className: "mt-auto", children: [
2267
+ /* @__PURE__ */ jsx(PoweredByBadge, { apiUrl }),
2268
+ /* @__PURE__ */ jsx(
2269
+ PromptInputBox,
2270
+ {
2271
+ onSend: handleSend,
2272
+ onStop: chat.stopGeneration,
2273
+ placeholder: "Type your message...",
2274
+ isLoading: chat.isLoading,
2275
+ className: "!bg-white !border-gray-300 backdrop-blur-md"
2276
+ }
2277
+ )
2278
+ ] })
2279
+ ] })
2280
+ ]
2281
+ }
2282
+ );
450
2283
  }
451
2284
  function CrowCopilot({
452
2285
  productId,
453
- apiUrl,
454
- model,
2286
+ apiUrl = "",
455
2287
  title = "Copilot",
456
2288
  position = "right",
457
2289
  width = 400,
458
2290
  showClose = false,
459
2291
  onClose,
460
- onIdentify,
461
- tools,
462
- className
2292
+ styles: propStyles,
2293
+ previewMode = false,
2294
+ className,
2295
+ onReady
463
2296
  }) {
464
- const contextClient = useCrowClientOptional();
465
- const standaloneClientRef = useRef(null);
466
- if (!contextClient && !standaloneClientRef.current && productId) {
467
- standaloneClientRef.current = new CrowClient({ productId, apiUrl, model });
468
- }
469
- const client = contextClient || standaloneClientRef.current;
470
- if (!client) {
471
- console.error(
472
- "[CrowCopilot] No client available. Either wrap in CrowProvider or provide productId prop."
473
- );
474
- return null;
475
- }
476
- useEffect(() => {
477
- return () => {
478
- if (standaloneClientRef.current) {
479
- standaloneClientRef.current.destroy();
2297
+ const { styles: styles2, isLoading: isLoadingStyles, agentName } = useCopilotStyles({
2298
+ productId,
2299
+ apiUrl,
2300
+ propStyles,
2301
+ skip: previewMode
2302
+ });
2303
+ const messagesContainerRef = useRef(null);
2304
+ const executeClientToolRef = useRef(null);
2305
+ const [showConversationList, setShowConversationList] = useState(false);
2306
+ const [isVerifiedUser, setIsVerifiedUser] = useState(false);
2307
+ const conversations = useConversations({ productId, apiUrl });
2308
+ const [shouldRestoreHistory, setShouldRestoreHistory] = useState(false);
2309
+ const hasRestoredHistoryRef = useRef(false);
2310
+ const chat = useChat({
2311
+ productId,
2312
+ apiUrl,
2313
+ onVerificationStatus: (isVerified) => {
2314
+ setIsVerifiedUser(isVerified);
2315
+ },
2316
+ onConversationId: () => {
2317
+ },
2318
+ onToolCall: (event) => {
2319
+ if (event.type === "client_call" && event.toolName) {
2320
+ console.log("[Crow Copilot] Executing client tool:", event.toolName, event.arguments);
2321
+ const result = executeClientToolRef.current?.(event.toolName, event.arguments || {});
2322
+ result?.then((r) => console.log("[Crow Copilot] Tool result:", r)).catch((e) => console.error("[Crow Copilot] Tool error:", e));
480
2323
  }
481
- };
482
- }, []);
483
- useEffect(() => {
484
- if (tools) {
485
- client.registerTools(tools);
2324
+ },
2325
+ onRestoredConversation: () => {
2326
+ setShouldRestoreHistory(true);
486
2327
  }
487
- }, [client, tools]);
2328
+ });
488
2329
  useEffect(() => {
489
- if (onIdentify) {
490
- onIdentify((options) => client.identify(options));
2330
+ if (shouldRestoreHistory && chat.conversationId && !hasRestoredHistoryRef.current) {
2331
+ hasRestoredHistoryRef.current = true;
2332
+ conversations.loadAnonymousConversationHistory(chat.conversationId).then((historyMessages) => {
2333
+ if (historyMessages.length > 0) {
2334
+ chat.loadMessages(historyMessages);
2335
+ }
2336
+ });
2337
+ }
2338
+ }, [shouldRestoreHistory, chat.conversationId, conversations, chat]);
2339
+ const { executeClientTool } = useCrowAPI({
2340
+ onIdentified: async () => {
2341
+ setIsVerifiedUser(true);
2342
+ await conversations.loadConversations();
2343
+ },
2344
+ onReset: () => {
2345
+ setIsVerifiedUser(false);
2346
+ chat.resetMessages();
491
2347
  }
492
- }, [client, onIdentify]);
493
- const { messages, isLoading, sendMessage, stop, clearMessages } = useChat({
494
- client
495
2348
  });
2349
+ executeClientToolRef.current = executeClientTool;
2350
+ useEffect(() => {
2351
+ if (!isLoadingStyles) {
2352
+ onReady?.();
2353
+ }
2354
+ }, [isLoadingStyles, onReady]);
2355
+ const handleSend = (message) => {
2356
+ if (!message.trim()) return;
2357
+ chat.sendMessage(message);
2358
+ };
2359
+ const handleNewChat = () => {
2360
+ chat.resetMessages();
2361
+ setShowConversationList(false);
2362
+ };
2363
+ const handleToggleHistory = () => {
2364
+ setShowConversationList(!showConversationList);
2365
+ if (!showConversationList) {
2366
+ conversations.loadConversations();
2367
+ }
2368
+ };
2369
+ const handleSelectConversation = async (convId) => {
2370
+ const historyMessages = await conversations.loadConversationHistory(convId);
2371
+ if (historyMessages.length > 0) {
2372
+ chat.loadMessages(historyMessages);
2373
+ chat.setConversationId(convId);
2374
+ } else {
2375
+ chat.resetMessages();
2376
+ }
2377
+ setShowConversationList(false);
2378
+ };
2379
+ const handleCloseConversationList = () => {
2380
+ setShowConversationList(false);
2381
+ };
496
2382
  const widthStyle = typeof width === "number" ? `${width}px` : width;
2383
+ return /* @__PURE__ */ jsx(
2384
+ CopilotStyleProvider,
2385
+ {
2386
+ styles: styles2,
2387
+ agentName: agentName || title,
2388
+ isLoading: isLoadingStyles,
2389
+ children: /* @__PURE__ */ jsxs(
2390
+ "div",
2391
+ {
2392
+ className: `flex flex-col h-full bg-white ${position === "left" ? "border-r" : "border-l"} border-gray-200 ${className || ""}`,
2393
+ style: {
2394
+ width: widthStyle,
2395
+ fontFamily: styles2.typography.fontFamily,
2396
+ fontSize: styles2.typography.fontSize,
2397
+ background: styles2.colors.background,
2398
+ borderColor: styles2.colors.border
2399
+ },
2400
+ children: [
2401
+ /* @__PURE__ */ jsxs(
2402
+ "div",
2403
+ {
2404
+ className: "flex items-center justify-between px-4 py-3 border-b",
2405
+ style: {
2406
+ height: styles2.dimensions.headerHeight,
2407
+ borderColor: styles2.colors.border
2408
+ },
2409
+ children: [
2410
+ /* @__PURE__ */ jsx(
2411
+ "span",
2412
+ {
2413
+ className: "font-semibold",
2414
+ style: {
2415
+ color: styles2.colors.text,
2416
+ fontSize: styles2.typography.headerFontSize
2417
+ },
2418
+ children: agentName || title
2419
+ }
2420
+ ),
2421
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2422
+ /* @__PURE__ */ jsx(
2423
+ "button",
2424
+ {
2425
+ onClick: handleNewChat,
2426
+ className: "p-1.5 hover:bg-gray-100 rounded transition-colors",
2427
+ "aria-label": "New Chat",
2428
+ title: "New Chat",
2429
+ children: /* @__PURE__ */ jsx(PlusIcon, { className: "w-4 h-4 text-gray-600" })
2430
+ }
2431
+ ),
2432
+ isVerifiedUser && /* @__PURE__ */ jsx(
2433
+ "button",
2434
+ {
2435
+ onClick: handleToggleHistory,
2436
+ className: `p-1.5 hover:bg-gray-100 rounded transition-colors ${showConversationList ? "bg-gray-100" : ""}`,
2437
+ "aria-label": "Conversation History",
2438
+ title: "Conversation History",
2439
+ children: /* @__PURE__ */ jsx(HistoryIcon, { className: "w-4 h-4 text-gray-600" })
2440
+ }
2441
+ ),
2442
+ showClose && onClose && /* @__PURE__ */ jsx(
2443
+ "button",
2444
+ {
2445
+ onClick: onClose,
2446
+ className: "p-1.5 hover:bg-gray-100 rounded transition-colors",
2447
+ "aria-label": "Close",
2448
+ children: /* @__PURE__ */ jsx(CloseIcon, { className: "w-4 h-4 text-gray-600" })
2449
+ }
2450
+ )
2451
+ ] })
2452
+ ]
2453
+ }
2454
+ ),
2455
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showConversationList && isVerifiedUser && /* @__PURE__ */ jsx(
2456
+ ConversationList,
2457
+ {
2458
+ conversations: conversations.conversations,
2459
+ currentConversationId: chat.conversationId,
2460
+ onSelect: handleSelectConversation,
2461
+ onClose: handleCloseConversationList
2462
+ }
2463
+ ) }),
2464
+ /* @__PURE__ */ jsx(MessagesContainer, { ref: messagesContainerRef, children: /* @__PURE__ */ jsx(
2465
+ MessageList,
2466
+ {
2467
+ messages: chat.messages,
2468
+ activeToolCalls: chat.activeToolCalls,
2469
+ isLoadingHistory: conversations.isLoadingHistory,
2470
+ isGenerating: chat.isLoading
2471
+ }
2472
+ ) }),
2473
+ /* @__PURE__ */ jsxs("div", { className: "p-3 border-t", style: { borderColor: styles2.colors.border }, children: [
2474
+ styles2.branding.showPoweredBy && /* @__PURE__ */ jsx(PoweredByBadge, { apiUrl }),
2475
+ /* @__PURE__ */ jsx(
2476
+ PromptInputBox,
2477
+ {
2478
+ onSend: handleSend,
2479
+ onStop: chat.stopGeneration,
2480
+ placeholder: "Ask anything...",
2481
+ isLoading: chat.isLoading,
2482
+ className: "!bg-white !border-gray-300"
2483
+ }
2484
+ )
2485
+ ] })
2486
+ ]
2487
+ }
2488
+ )
2489
+ }
2490
+ );
2491
+ }
2492
+ function PlusIcon({ className }) {
497
2493
  return /* @__PURE__ */ jsxs(
498
- "div",
2494
+ "svg",
499
2495
  {
500
- className: clsx(
501
- "crow-flex crow-flex-col crow-h-full",
502
- "crow-bg-white crow-border-gray-200",
503
- position === "left" ? "crow-border-r" : "crow-border-l",
504
- className
505
- ),
506
- style: { width: widthStyle },
2496
+ xmlns: "http://www.w3.org/2000/svg",
2497
+ width: "24",
2498
+ height: "24",
2499
+ viewBox: "0 0 24 24",
2500
+ fill: "none",
2501
+ stroke: "currentColor",
2502
+ strokeWidth: "2",
2503
+ strokeLinecap: "round",
2504
+ strokeLinejoin: "round",
2505
+ className,
507
2506
  children: [
508
- /* @__PURE__ */ jsx(
509
- WidgetHeader,
510
- {
511
- title,
512
- onNewChat: clearMessages,
513
- onClose,
514
- showNewChat: true,
515
- showClose
516
- }
517
- ),
518
- /* @__PURE__ */ jsx(
519
- MessageList,
520
- {
521
- messages,
522
- isLoading,
523
- className: "crow-flex-1"
524
- }
525
- ),
526
- /* @__PURE__ */ jsx(
527
- PromptInput,
528
- {
529
- onSend: sendMessage,
530
- onStop: stop,
531
- isLoading,
532
- placeholder: "Ask anything..."
533
- }
534
- )
2507
+ /* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
2508
+ /* @__PURE__ */ jsx("path", { d: "M12 5v14" })
535
2509
  ]
536
2510
  }
537
2511
  );
538
2512
  }
539
- function useIdentity({ client }) {
540
- const [isIdentified, setIsIdentified] = useState(client.isIdentified());
541
- const [isVerified, setIsVerified] = useState(client.isVerified());
542
- useEffect(() => {
543
- client.on({
544
- onVerificationStatus: (verified) => {
545
- setIsVerified(verified);
546
- }
547
- });
548
- setIsIdentified(client.isIdentified());
549
- setIsVerified(client.isVerified());
550
- }, [client]);
551
- const identify = useCallback(
552
- (options) => {
553
- client.identify(options);
554
- setIsIdentified(true);
555
- },
556
- [client]
2513
+ function HistoryIcon({ className }) {
2514
+ return /* @__PURE__ */ jsxs(
2515
+ "svg",
2516
+ {
2517
+ xmlns: "http://www.w3.org/2000/svg",
2518
+ width: "24",
2519
+ height: "24",
2520
+ viewBox: "0 0 24 24",
2521
+ fill: "none",
2522
+ stroke: "currentColor",
2523
+ strokeWidth: "2",
2524
+ strokeLinecap: "round",
2525
+ strokeLinejoin: "round",
2526
+ className,
2527
+ children: [
2528
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
2529
+ /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" }),
2530
+ /* @__PURE__ */ jsx("path", { d: "M12 7v5l4 2" })
2531
+ ]
2532
+ }
557
2533
  );
558
- const resetUser = useCallback(() => {
559
- client.resetUser();
560
- setIsIdentified(false);
561
- setIsVerified(false);
562
- }, [client]);
563
- return {
564
- isIdentified,
565
- isVerified,
566
- identify,
567
- resetUser
568
- };
569
2534
  }
570
- function useConversations({
571
- client
572
- }) {
573
- const [conversations, setConversations] = useState([]);
574
- const [isLoadingConversations, setIsLoadingConversations] = useState(false);
575
- const [isLoadingHistory, setIsLoadingHistory] = useState(false);
576
- const loadConversations = useCallback(async () => {
577
- setIsLoadingConversations(true);
578
- try {
579
- const convs = await client.getConversations();
580
- setConversations(convs);
581
- } finally {
582
- setIsLoadingConversations(false);
2535
+ function CloseIcon({ className }) {
2536
+ return /* @__PURE__ */ jsxs(
2537
+ "svg",
2538
+ {
2539
+ xmlns: "http://www.w3.org/2000/svg",
2540
+ width: "24",
2541
+ height: "24",
2542
+ viewBox: "0 0 24 24",
2543
+ fill: "none",
2544
+ stroke: "currentColor",
2545
+ strokeWidth: "2",
2546
+ strokeLinecap: "round",
2547
+ strokeLinejoin: "round",
2548
+ className,
2549
+ children: [
2550
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
2551
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
2552
+ ]
583
2553
  }
584
- }, [client]);
585
- const loadHistory = useCallback(
586
- async (conversationId) => {
587
- setIsLoadingHistory(true);
588
- try {
589
- return await client.loadHistory(conversationId);
590
- } finally {
591
- setIsLoadingHistory(false);
592
- }
593
- },
594
- [client]
595
2554
  );
596
- const switchConversation = useCallback(
597
- async (conversationId) => {
598
- setIsLoadingHistory(true);
599
- try {
600
- await client.switchConversation(conversationId);
601
- } finally {
602
- setIsLoadingHistory(false);
603
- }
604
- },
605
- [client]
606
- );
607
- const startNewConversation = useCallback(() => {
608
- client.clearMessages();
2555
+ }
2556
+ var CrowContext = createContext(null);
2557
+ function CrowProvider({
2558
+ children,
2559
+ productId,
2560
+ apiUrl,
2561
+ model
2562
+ }) {
2563
+ const clientRef = useRef(null);
2564
+ if (!clientRef.current) {
2565
+ clientRef.current = new CrowClient({ productId, apiUrl, model });
2566
+ }
2567
+ const client = clientRef.current;
2568
+ useEffect(() => {
2569
+ return () => {
2570
+ client.destroy();
2571
+ };
609
2572
  }, [client]);
610
- return {
611
- conversations,
612
- isLoadingConversations,
613
- isLoadingHistory,
614
- currentConversationId: client.conversationId,
615
- loadConversations,
616
- loadHistory,
617
- switchConversation,
618
- startNewConversation
619
- };
2573
+ const value = useMemo(() => ({ client }), [client]);
2574
+ return /* @__PURE__ */ jsx(CrowContext.Provider, { value, children });
620
2575
  }
621
2576
 
622
- export { ChatBubble, CrowCopilot, CrowProvider, CrowWidget, MessageBubble, MessageList, PromptInput, WidgetHeader, useChat, useConversations, useCrowClient, useCrowClientOptional, useIdentity };
2577
+ export { AVAILABLE_MODELS, ChatBubble, ConversationList, CopilotStyleProvider, CrowCopilot, CrowProvider, CrowWidget, DEFAULT_COPILOT_STYLES, DEFAULT_MODEL, DEFAULT_WELCOME_MESSAGE, DEFAULT_WIDGET_STYLES, GlassCard, LoadingHistory, MESSAGES_CONTAINER_ID, MessageBubble, MessageList, MessagesContainer, ModelSelector, PoweredByBadge, PromptInputBox, ReasoningTrace, StreamingText, ThinkingIndicator, WidgetHeader, WidgetShell, WidgetStyleProvider, WorkflowPanel, clearStyleCache, mergeCopilotStyles, mergeWidgetStyles, stylesToCSSVariables, useChat, useConversations, useCopilotStyleContext, useCopilotStyles, useCopilotStyles2 as useCopilotStylesContext, useCrowAPI, usePreviewCopilotStyles, usePreviewWidgetStyles, useWidgetStyleContext, useWidgetStyles, useWidgetStyles2 as useWidgetStylesContext, useWorkflow };
623
2578
  //# sourceMappingURL=index.js.map
624
2579
  //# sourceMappingURL=index.js.map