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