@usecrow/ui 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -182,6 +182,9 @@ interface WidgetConfigResponse {
182
182
  copilotStyles: CopilotStyleConfig;
183
183
  browserUseEnabled?: boolean;
184
184
  showThinking?: boolean;
185
+ persistAnonymousConversations?: boolean;
186
+ /** Custom welcome message shown when chat opens. Null uses SDK default. */
187
+ welcomeMessage?: string | null;
185
188
  }
186
189
 
187
190
  /** Identity data passed to the identify function */
@@ -212,6 +215,8 @@ interface CrowWidgetProps {
212
215
  previewMode?: boolean;
213
216
  /** Whether to show AI thinking/reasoning to users (overrides API setting if provided) */
214
217
  showThinking?: boolean;
218
+ /** Custom welcome message (overrides API setting if provided) */
219
+ welcomeMessage?: string;
215
220
  /** Callback when widget is ready */
216
221
  onReady?: () => void;
217
222
  /**
@@ -222,7 +227,7 @@ interface CrowWidgetProps {
222
227
  /** Client-side tools the agent can call */
223
228
  tools?: ToolsMap;
224
229
  }
225
- declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, onReady, onIdentify, tools, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
230
+ declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, welcomeMessage: welcomeMessageProp, onReady, onIdentify, tools, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
226
231
 
227
232
  interface CrowCopilotProps {
228
233
  /** Product ID for this copilot */
@@ -529,13 +534,17 @@ interface ToolCallEvent {
529
534
  interface UseChatOptions {
530
535
  productId: string;
531
536
  apiUrl?: string;
537
+ /** Whether to persist anonymous conversations across page refreshes (default: true) */
538
+ persistAnonymousConversations?: boolean;
539
+ /** Custom welcome message (uses SDK default if not provided) */
540
+ welcomeMessage?: string;
532
541
  onVerificationStatus?: (isVerified: boolean) => void;
533
542
  onConversationId?: (id: string) => void;
534
543
  onWorkflowEvent?: (event: WorkflowEvent) => void;
535
544
  onToolCall?: (toolCall: ToolCallEvent) => void;
536
545
  onRestoredConversation?: (conversationId: string) => void;
537
546
  }
538
- declare function useChat({ productId, apiUrl, onVerificationStatus, onConversationId, onWorkflowEvent, onToolCall, onRestoredConversation, }: UseChatOptions): {
547
+ declare function useChat({ productId, apiUrl, persistAnonymousConversations, welcomeMessage, onVerificationStatus, onConversationId, onWorkflowEvent, onToolCall, onRestoredConversation, }: UseChatOptions): {
539
548
  messages: Message[];
540
549
  isLoading: boolean;
541
550
  activeToolCalls: ToolCall[];
@@ -636,6 +645,10 @@ interface UseWidgetStylesResult {
636
645
  browserUseEnabled: boolean;
637
646
  /** Whether to show thinking/reasoning to users */
638
647
  showThinking: boolean;
648
+ /** Whether to persist anonymous conversations across page refreshes (undefined while loading) */
649
+ persistAnonymousConversations: boolean | undefined;
650
+ /** Custom welcome message (undefined uses SDK default) */
651
+ welcomeMessage: string | undefined;
639
652
  /** Refetch styles from API */
640
653
  refetch: () => Promise<void>;
641
654
  }
@@ -660,6 +673,10 @@ interface UseCopilotStylesResult {
660
673
  error: Error | null;
661
674
  /** Agent name from product config */
662
675
  agentName: string;
676
+ /** Whether to persist anonymous conversations across page refreshes (undefined while loading) */
677
+ persistAnonymousConversations: boolean | undefined;
678
+ /** Custom welcome message (undefined uses SDK default) */
679
+ welcomeMessage: string | undefined;
663
680
  /** Refetch styles from API */
664
681
  refetch: () => Promise<void>;
665
682
  }
package/dist/index.d.ts CHANGED
@@ -182,6 +182,9 @@ interface WidgetConfigResponse {
182
182
  copilotStyles: CopilotStyleConfig;
183
183
  browserUseEnabled?: boolean;
184
184
  showThinking?: boolean;
185
+ persistAnonymousConversations?: boolean;
186
+ /** Custom welcome message shown when chat opens. Null uses SDK default. */
187
+ welcomeMessage?: string | null;
185
188
  }
186
189
 
187
190
  /** Identity data passed to the identify function */
@@ -212,6 +215,8 @@ interface CrowWidgetProps {
212
215
  previewMode?: boolean;
213
216
  /** Whether to show AI thinking/reasoning to users (overrides API setting if provided) */
214
217
  showThinking?: boolean;
218
+ /** Custom welcome message (overrides API setting if provided) */
219
+ welcomeMessage?: string;
215
220
  /** Callback when widget is ready */
216
221
  onReady?: () => void;
217
222
  /**
@@ -222,7 +227,7 @@ interface CrowWidgetProps {
222
227
  /** Client-side tools the agent can call */
223
228
  tools?: ToolsMap;
224
229
  }
225
- declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, onReady, onIdentify, tools, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
230
+ declare function CrowWidget({ productId, apiUrl, variant, styles: propStyles, previewMode, showThinking: showThinkingProp, welcomeMessage: welcomeMessageProp, onReady, onIdentify, tools, }: CrowWidgetProps): react_jsx_runtime.JSX.Element;
226
231
 
227
232
  interface CrowCopilotProps {
228
233
  /** Product ID for this copilot */
@@ -529,13 +534,17 @@ interface ToolCallEvent {
529
534
  interface UseChatOptions {
530
535
  productId: string;
531
536
  apiUrl?: string;
537
+ /** Whether to persist anonymous conversations across page refreshes (default: true) */
538
+ persistAnonymousConversations?: boolean;
539
+ /** Custom welcome message (uses SDK default if not provided) */
540
+ welcomeMessage?: string;
532
541
  onVerificationStatus?: (isVerified: boolean) => void;
533
542
  onConversationId?: (id: string) => void;
534
543
  onWorkflowEvent?: (event: WorkflowEvent) => void;
535
544
  onToolCall?: (toolCall: ToolCallEvent) => void;
536
545
  onRestoredConversation?: (conversationId: string) => void;
537
546
  }
538
- declare function useChat({ productId, apiUrl, onVerificationStatus, onConversationId, onWorkflowEvent, onToolCall, onRestoredConversation, }: UseChatOptions): {
547
+ declare function useChat({ productId, apiUrl, persistAnonymousConversations, welcomeMessage, onVerificationStatus, onConversationId, onWorkflowEvent, onToolCall, onRestoredConversation, }: UseChatOptions): {
539
548
  messages: Message[];
540
549
  isLoading: boolean;
541
550
  activeToolCalls: ToolCall[];
@@ -636,6 +645,10 @@ interface UseWidgetStylesResult {
636
645
  browserUseEnabled: boolean;
637
646
  /** Whether to show thinking/reasoning to users */
638
647
  showThinking: boolean;
648
+ /** Whether to persist anonymous conversations across page refreshes (undefined while loading) */
649
+ persistAnonymousConversations: boolean | undefined;
650
+ /** Custom welcome message (undefined uses SDK default) */
651
+ welcomeMessage: string | undefined;
639
652
  /** Refetch styles from API */
640
653
  refetch: () => Promise<void>;
641
654
  }
@@ -660,6 +673,10 @@ interface UseCopilotStylesResult {
660
673
  error: Error | null;
661
674
  /** Agent name from product config */
662
675
  agentName: string;
676
+ /** Whether to persist anonymous conversations across page refreshes (undefined while loading) */
677
+ persistAnonymousConversations: boolean | undefined;
678
+ /** Custom welcome message (undefined uses SDK default) */
679
+ welcomeMessage: string | undefined;
663
680
  /** Refetch styles from API */
664
681
  refetch: () => Promise<void>;
665
682
  }
package/dist/index.js CHANGED
@@ -42,41 +42,68 @@ var MESSAGES_CONTAINER_ID = "crow-messages-container";
42
42
 
43
43
  // src/hooks/useChat.ts
44
44
  var getConversationStorageKey = (productId) => `crow_conv_${productId}`;
45
+ var inMemoryConversations = {};
45
46
  function useChat({
46
47
  productId,
47
48
  apiUrl = "",
49
+ persistAnonymousConversations,
50
+ welcomeMessage,
48
51
  onVerificationStatus,
49
52
  onConversationId,
50
53
  onWorkflowEvent,
51
54
  onToolCall,
52
55
  onRestoredConversation
53
56
  }) {
57
+ const effectiveWelcomeMessage = welcomeMessage || DEFAULT_WELCOME_MESSAGE;
54
58
  const [messages, setMessages] = useState([
55
59
  {
56
60
  id: "welcome",
57
- content: DEFAULT_WELCOME_MESSAGE,
61
+ content: effectiveWelcomeMessage,
58
62
  isBot: true,
59
63
  timestamp: /* @__PURE__ */ new Date()
60
64
  }
61
65
  ]);
62
66
  const [isLoading, setIsLoading] = useState(false);
63
67
  const [activeToolCalls, setActiveToolCalls] = useState([]);
64
- const [conversationId, setConversationId] = useState(() => {
65
- try {
66
- return localStorage.getItem(getConversationStorageKey(productId));
67
- } catch {
68
- return null;
69
- }
70
- });
68
+ const [conversationId, setConversationId] = useState(null);
71
69
  const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL);
72
70
  const abortControllerRef = useRef(null);
73
- const hasRestoredRef = useRef(false);
71
+ const hasInitializedConversationRef = useRef(false);
74
72
  useEffect(() => {
75
- if (conversationId && onRestoredConversation && !hasRestoredRef.current) {
76
- hasRestoredRef.current = true;
77
- onRestoredConversation(conversationId);
73
+ if (messages.length === 1 && messages[0].id === "welcome" && !conversationId) {
74
+ setMessages([
75
+ {
76
+ id: "welcome",
77
+ content: effectiveWelcomeMessage,
78
+ isBot: true,
79
+ timestamp: /* @__PURE__ */ new Date()
80
+ }
81
+ ]);
78
82
  }
79
- }, []);
83
+ }, [effectiveWelcomeMessage]);
84
+ useEffect(() => {
85
+ if (hasInitializedConversationRef.current || persistAnonymousConversations === void 0) {
86
+ return;
87
+ }
88
+ hasInitializedConversationRef.current = true;
89
+ try {
90
+ if (persistAnonymousConversations) {
91
+ const convId = localStorage.getItem(getConversationStorageKey(productId));
92
+ if (convId) {
93
+ setConversationId(convId);
94
+ onRestoredConversation?.(convId);
95
+ }
96
+ } else {
97
+ const convId = inMemoryConversations[productId];
98
+ if (convId) {
99
+ setConversationId(convId);
100
+ onRestoredConversation?.(convId);
101
+ }
102
+ }
103
+ } catch (e) {
104
+ console.error("[Crow] Error initializing conversation:", e);
105
+ }
106
+ }, [persistAnonymousConversations, productId, onRestoredConversation]);
80
107
  const streamFromBackend = useCallback(
81
108
  async (message, botMsgId) => {
82
109
  let accumulatedText = "";
@@ -134,12 +161,16 @@ function useChat({
134
161
  case "conversation_id":
135
162
  if (parsed.conversation_id) {
136
163
  setConversationId(parsed.conversation_id);
137
- try {
138
- localStorage.setItem(
139
- getConversationStorageKey(productId),
140
- parsed.conversation_id
141
- );
142
- } catch {
164
+ if (persistAnonymousConversations) {
165
+ try {
166
+ localStorage.setItem(
167
+ getConversationStorageKey(productId),
168
+ parsed.conversation_id
169
+ );
170
+ } catch {
171
+ }
172
+ } else {
173
+ inMemoryConversations[productId] = parsed.conversation_id;
143
174
  }
144
175
  onConversationId?.(parsed.conversation_id);
145
176
  }
@@ -277,7 +308,7 @@ function useChat({
277
308
  abortControllerRef.current = null;
278
309
  }
279
310
  },
280
- [apiUrl, productId, conversationId, selectedModel, onVerificationStatus, onConversationId, onWorkflowEvent, onToolCall]
311
+ [apiUrl, productId, conversationId, selectedModel, persistAnonymousConversations, onVerificationStatus, onConversationId, onWorkflowEvent, onToolCall]
281
312
  );
282
313
  const sendMessage = useCallback(
283
314
  (content) => {
@@ -321,17 +352,18 @@ function useChat({
321
352
  setMessages([
322
353
  {
323
354
  id: "welcome",
324
- content: DEFAULT_WELCOME_MESSAGE,
355
+ content: effectiveWelcomeMessage,
325
356
  isBot: true,
326
357
  timestamp: /* @__PURE__ */ new Date()
327
358
  }
328
359
  ]);
329
360
  setConversationId(null);
361
+ delete inMemoryConversations[productId];
330
362
  try {
331
363
  localStorage.removeItem(getConversationStorageKey(productId));
332
364
  } catch {
333
365
  }
334
- }, [productId]);
366
+ }, [productId, effectiveWelcomeMessage]);
335
367
  const loadMessages = useCallback((historyMessages) => {
336
368
  setMessages(historyMessages);
337
369
  }, []);
@@ -1016,6 +1048,12 @@ function useWidgetStyles({
1016
1048
  const [showThinking, setShowThinking] = useState(
1017
1049
  styleCache.get(key)?.showThinking ?? true
1018
1050
  );
1051
+ const [persistAnonymousConversations, setPersistAnonymousConversations] = useState(
1052
+ styleCache.has(key) ? styleCache.get(key)?.persistAnonymousConversations ?? true : void 0
1053
+ );
1054
+ const [welcomeMessage, setWelcomeMessage] = useState(
1055
+ styleCache.get(key)?.welcomeMessage ?? void 0
1056
+ );
1019
1057
  const hasFetchedRef = useRef(false);
1020
1058
  const fetchStyles = async () => {
1021
1059
  if (skip) return;
@@ -1028,6 +1066,8 @@ function useWidgetStyles({
1028
1066
  setAgentName(config.agentName || "Assistant");
1029
1067
  setBrowserUseEnabled(config.browserUseEnabled || false);
1030
1068
  setShowThinking(config.showThinking ?? true);
1069
+ setPersistAnonymousConversations(config.persistAnonymousConversations ?? true);
1070
+ setWelcomeMessage(config.welcomeMessage ?? void 0);
1031
1071
  } catch (err) {
1032
1072
  console.error("[CrowWidget] Failed to fetch styles:", err);
1033
1073
  setError(err instanceof Error ? err : new Error(String(err)));
@@ -1043,6 +1083,8 @@ function useWidgetStyles({
1043
1083
  setAgentName(cached.agentName || "Assistant");
1044
1084
  setBrowserUseEnabled(cached.browserUseEnabled || false);
1045
1085
  setShowThinking(cached.showThinking ?? true);
1086
+ setPersistAnonymousConversations(cached.persistAnonymousConversations ?? true);
1087
+ setWelcomeMessage(cached.welcomeMessage ?? void 0);
1046
1088
  setIsLoading(false);
1047
1089
  return;
1048
1090
  }
@@ -1057,6 +1099,8 @@ function useWidgetStyles({
1057
1099
  agentName,
1058
1100
  browserUseEnabled,
1059
1101
  showThinking,
1102
+ persistAnonymousConversations,
1103
+ welcomeMessage,
1060
1104
  refetch: fetchStyles
1061
1105
  };
1062
1106
  }
@@ -1076,6 +1120,12 @@ function useCopilotStyles({
1076
1120
  const [agentName, setAgentName] = useState(
1077
1121
  styleCache.get(key)?.agentName || "Assistant"
1078
1122
  );
1123
+ const [persistAnonymousConversations, setPersistAnonymousConversations] = useState(
1124
+ styleCache.has(key) ? styleCache.get(key)?.persistAnonymousConversations ?? true : void 0
1125
+ );
1126
+ const [welcomeMessage, setWelcomeMessage] = useState(
1127
+ styleCache.get(key)?.welcomeMessage ?? void 0
1128
+ );
1079
1129
  const hasFetchedRef = useRef(false);
1080
1130
  const fetchStyles = async () => {
1081
1131
  if (skip) return;
@@ -1086,6 +1136,8 @@ function useCopilotStyles({
1086
1136
  styleCache.set(key, config);
1087
1137
  setDbStyles(config.copilotStyles);
1088
1138
  setAgentName(config.agentName || "Assistant");
1139
+ setPersistAnonymousConversations(config.persistAnonymousConversations ?? true);
1140
+ setWelcomeMessage(config.welcomeMessage ?? void 0);
1089
1141
  } catch (err) {
1090
1142
  console.error("[CrowCopilot] Failed to fetch styles:", err);
1091
1143
  setError(err instanceof Error ? err : new Error(String(err)));
@@ -1099,6 +1151,8 @@ function useCopilotStyles({
1099
1151
  if (cached) {
1100
1152
  setDbStyles(cached.copilotStyles);
1101
1153
  setAgentName(cached.agentName || "Assistant");
1154
+ setPersistAnonymousConversations(cached.persistAnonymousConversations ?? true);
1155
+ setWelcomeMessage(cached.welcomeMessage ?? void 0);
1102
1156
  setIsLoading(false);
1103
1157
  return;
1104
1158
  }
@@ -1111,6 +1165,8 @@ function useCopilotStyles({
1111
1165
  isLoading,
1112
1166
  error,
1113
1167
  agentName,
1168
+ persistAnonymousConversations,
1169
+ welcomeMessage,
1114
1170
  refetch: fetchStyles
1115
1171
  };
1116
1172
  }
@@ -2423,16 +2479,27 @@ function CrowWidget({
2423
2479
  styles: propStyles,
2424
2480
  previewMode = false,
2425
2481
  showThinking: showThinkingProp,
2482
+ welcomeMessage: welcomeMessageProp,
2426
2483
  onReady,
2427
2484
  onIdentify,
2428
2485
  tools
2429
2486
  }) {
2430
- const { styles, isLoading: isLoadingStyles, agentName, browserUseEnabled, showThinking: showThinkingFromAPI } = useWidgetStyles({
2487
+ const {
2488
+ styles,
2489
+ isLoading: isLoadingStyles,
2490
+ agentName,
2491
+ browserUseEnabled,
2492
+ showThinking: showThinkingFromAPI,
2493
+ persistAnonymousConversations,
2494
+ welcomeMessage: welcomeMessageFromAPI
2495
+ } = useWidgetStyles({
2431
2496
  productId,
2432
2497
  apiUrl,
2433
2498
  propStyles,
2434
2499
  skip: previewMode
2435
2500
  });
2501
+ const welcomeMessage = welcomeMessageProp ?? welcomeMessageFromAPI;
2502
+ console.log("[Crow Widget] persistAnonymousConversations from API:", persistAnonymousConversations, "isLoading:", isLoadingStyles);
2436
2503
  const showThinking = showThinkingProp ?? showThinkingFromAPI;
2437
2504
  const [autoTools, setAutoTools] = useState({});
2438
2505
  const cssVars = stylesToCssVars(styles);
@@ -2460,6 +2527,8 @@ function CrowWidget({
2460
2527
  const chat = useChat({
2461
2528
  productId,
2462
2529
  apiUrl,
2530
+ persistAnonymousConversations,
2531
+ welcomeMessage,
2463
2532
  onVerificationStatus: (isVerified) => {
2464
2533
  setIsVerifiedUser(isVerified);
2465
2534
  },
@@ -2704,7 +2773,7 @@ function CrowCopilot({
2704
2773
  className,
2705
2774
  onReady
2706
2775
  }) {
2707
- const { styles, isLoading: isLoadingStyles, agentName } = useCopilotStyles({
2776
+ const { styles, isLoading: isLoadingStyles, agentName, persistAnonymousConversations, welcomeMessage } = useCopilotStyles({
2708
2777
  productId,
2709
2778
  apiUrl,
2710
2779
  propStyles,
@@ -2720,6 +2789,8 @@ function CrowCopilot({
2720
2789
  const chat = useChat({
2721
2790
  productId,
2722
2791
  apiUrl,
2792
+ persistAnonymousConversations,
2793
+ welcomeMessage,
2723
2794
  onVerificationStatus: (isVerified) => {
2724
2795
  setIsVerifiedUser(isVerified);
2725
2796
  },