@usecrow/ui 0.1.40 → 0.1.42

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
@@ -1305,6 +1305,9 @@ function useCopilotStyles({
1305
1305
  const [agentName, setAgentName] = React3.useState(
1306
1306
  styleCache.get(key)?.agentName || "Assistant"
1307
1307
  );
1308
+ const [browserUseEnabled, setBrowserUseEnabled] = React3.useState(
1309
+ styleCache.get(key)?.browserUseEnabled || false
1310
+ );
1308
1311
  const [pageNavigationEnabled, setPageNavigationEnabled] = React3.useState(
1309
1312
  styleCache.get(key)?.pageNavigationEnabled || false
1310
1313
  );
@@ -1328,6 +1331,7 @@ function useCopilotStyles({
1328
1331
  styleCache.set(key, config);
1329
1332
  setDbStyles(config.copilotStyles);
1330
1333
  setAgentName(config.agentName || "Assistant");
1334
+ setBrowserUseEnabled(config.browserUseEnabled || false);
1331
1335
  setPageNavigationEnabled(config.pageNavigationEnabled || false);
1332
1336
  setPageNavigationRoutes(config.pageNavigationRoutes || []);
1333
1337
  setPersistAnonymousConversations(config.persistAnonymousConversations ?? true);
@@ -1346,6 +1350,7 @@ function useCopilotStyles({
1346
1350
  if (cached) {
1347
1351
  setDbStyles(cached.copilotStyles);
1348
1352
  setAgentName(cached.agentName || "Assistant");
1353
+ setBrowserUseEnabled(cached.browserUseEnabled || false);
1349
1354
  setPageNavigationEnabled(cached.pageNavigationEnabled || false);
1350
1355
  setPageNavigationRoutes(cached.pageNavigationRoutes || []);
1351
1356
  setPersistAnonymousConversations(cached.persistAnonymousConversations ?? true);
@@ -1363,6 +1368,7 @@ function useCopilotStyles({
1363
1368
  isLoading,
1364
1369
  error,
1365
1370
  agentName,
1371
+ browserUseEnabled,
1366
1372
  pageNavigationEnabled,
1367
1373
  pageNavigationRoutes,
1368
1374
  persistAnonymousConversations,
@@ -1848,7 +1854,14 @@ function ReasoningTrace({
1848
1854
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "crow-flex crow-justify-start crow-mb-2 crow-w-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "crow-w-full crow-space-y-1.5", children: [
1849
1855
  isWaiting && !hasThinking && /* @__PURE__ */ jsxRuntime.jsx(WaitingIndicator, {}),
1850
1856
  hasThinking && /* @__PURE__ */ jsxRuntime.jsx(ThinkingBlock, { thinking, isComplete }),
1851
- toolCalls.map((tool) => /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { toolCall: tool, toolRenderers }, tool.id))
1857
+ toolCalls.map((tool) => /* @__PURE__ */ jsxRuntime.jsx(
1858
+ ToolCallBlock,
1859
+ {
1860
+ toolCall: tool,
1861
+ toolRenderers
1862
+ },
1863
+ tool.id
1864
+ ))
1852
1865
  ] }) });
1853
1866
  }
1854
1867
  function WaitingIndicator() {
@@ -2924,7 +2937,9 @@ function CrowWidget({
2924
2937
  onIdentify,
2925
2938
  tools,
2926
2939
  navigate,
2927
- onToolResult
2940
+ onToolResult,
2941
+ getIdentityToken,
2942
+ context
2928
2943
  }) {
2929
2944
  const {
2930
2945
  styles,
@@ -3102,6 +3117,35 @@ function CrowWidget({
3102
3117
  onIdentify(identify);
3103
3118
  }
3104
3119
  }, [isLoadingStyles, onIdentify]);
3120
+ React3.useEffect(() => {
3121
+ if (!getIdentityToken || isLoadingStyles) return;
3122
+ let cancelled = false;
3123
+ const identify = async () => {
3124
+ try {
3125
+ const token = await getIdentityToken();
3126
+ if (!cancelled && token) {
3127
+ window.crow?.("identify", { token });
3128
+ }
3129
+ } catch (e) {
3130
+ console.error("[Crow] getIdentityToken failed:", e);
3131
+ }
3132
+ };
3133
+ identify();
3134
+ const handleRefresh = () => identify();
3135
+ window.addEventListener("crow:token-refresh-needed", handleRefresh);
3136
+ return () => {
3137
+ cancelled = true;
3138
+ window.removeEventListener("crow:token-refresh-needed", handleRefresh);
3139
+ };
3140
+ }, [getIdentityToken, isLoadingStyles]);
3141
+ React3.useEffect(() => {
3142
+ if (typeof window === "undefined") return;
3143
+ if (context && Object.keys(context).length > 0) {
3144
+ window.__crow_page_context = context;
3145
+ } else {
3146
+ window.__crow_page_context = void 0;
3147
+ }
3148
+ }, [context]);
3105
3149
  const handleBrowserConfirmation = React3.useCallback(
3106
3150
  (instruction) => {
3107
3151
  return new Promise((resolve) => {
@@ -3463,12 +3507,15 @@ function CrowCopilot({
3463
3507
  onReady,
3464
3508
  navigate,
3465
3509
  onToolResult,
3466
- toolRenderers
3510
+ toolRenderers,
3511
+ getIdentityToken,
3512
+ context
3467
3513
  }) {
3468
3514
  const {
3469
3515
  styles,
3470
3516
  isLoading: isLoadingStyles,
3471
3517
  agentName: agentNameFromAPI,
3518
+ browserUseEnabled,
3472
3519
  pageNavigationEnabled,
3473
3520
  pageNavigationRoutes,
3474
3521
  persistAnonymousConversations,
@@ -3483,6 +3530,20 @@ function CrowCopilot({
3483
3530
  const agentName = agentNameProp ?? agentNameFromAPI ?? title;
3484
3531
  const welcomeMessage = welcomeMessageProp ?? welcomeMessageFromAPI;
3485
3532
  const [autoTools, setAutoTools] = React3.useState({});
3533
+ const browserUseLoaded = autoTools.browser_use;
3534
+ React3.useEffect(() => {
3535
+ if (browserUseEnabled && !isLoadingStyles && !browserUseLoaded) {
3536
+ import('@usecrow/client/browser').then(({ createBrowserUseTool }) => {
3537
+ setAutoTools((prev) => ({
3538
+ ...prev,
3539
+ browser_use: createBrowserUseTool({ productId, apiUrl })
3540
+ }));
3541
+ console.log("[Crow] browser_use tool auto-loaded");
3542
+ }).catch((err) => {
3543
+ console.warn("[Crow] Failed to load browser_use:", err);
3544
+ });
3545
+ }
3546
+ }, [browserUseEnabled, isLoadingStyles, productId, apiUrl, browserUseLoaded]);
3486
3547
  React3.useEffect(() => {
3487
3548
  if (pageNavigationEnabled && pageNavigationRoutes.length > 0 && !isLoadingStyles) {
3488
3549
  import('@usecrow/client').then(({ createNavigateToPageTool }) => {
@@ -3515,8 +3576,10 @@ function CrowCopilot({
3515
3576
  const [activeLocalTabId, setActiveLocalTabId] = React3.useState("local-1");
3516
3577
  const [localTabMessages, setLocalTabMessages] = React3.useState({});
3517
3578
  const [localTabCounter, setLocalTabCounter] = React3.useState(1);
3579
+ const [isBrowserUseActive, setIsBrowserUseActive] = React3.useState(false);
3518
3580
  const [pendingConfirmation, setPendingConfirmation] = React3.useState(null);
3519
- const [pendingQuestion, setPendingQuestion] = React3.useState(null);
3581
+ const [askUserResolver, setAskUserResolver] = React3.useState(null);
3582
+ const [browserQuestion, setBrowserQuestion] = React3.useState(null);
3520
3583
  const conversations = useConversations({ productId, apiUrl });
3521
3584
  const [shouldRestoreHistory, setShouldRestoreHistory] = React3.useState(false);
3522
3585
  const hasRestoredHistoryRef = React3.useRef(false);
@@ -3657,10 +3720,25 @@ function CrowCopilot({
3657
3720
  );
3658
3721
  const handleBrowserQuestion = React3.useCallback(
3659
3722
  (question) => {
3660
- return new Promise((resolve) => {
3661
- setPendingQuestion({ question, resolve });
3723
+ return new Promise((resolve, reject) => {
3724
+ setAskUserResolver((prev) => {
3725
+ if (prev) {
3726
+ prev.reject(new Error("Superseded by new browser question"));
3727
+ }
3728
+ return { resolve, reject };
3729
+ });
3730
+ setBrowserQuestion(question);
3731
+ chat.addMessage("assistant", question);
3662
3732
  });
3663
3733
  },
3734
+ [chat]
3735
+ );
3736
+ const handleBrowserProgress = React3.useCallback(
3737
+ (step, maxSteps) => {
3738
+ if (step >= maxSteps || step === -1) {
3739
+ setIsBrowserUseActive(false);
3740
+ }
3741
+ },
3664
3742
  []
3665
3743
  );
3666
3744
  const handleExitWorkflow = async () => {
@@ -3669,21 +3747,79 @@ function CrowCopilot({
3669
3747
  React3.useEffect(() => {
3670
3748
  window.__crow_browser_callbacks = {
3671
3749
  onConfirmation: handleBrowserConfirmation,
3672
- onQuestion: handleBrowserQuestion
3750
+ onQuestion: handleBrowserQuestion,
3751
+ onProgress: handleBrowserProgress
3673
3752
  };
3674
3753
  return () => {
3675
3754
  delete window.__crow_browser_callbacks;
3676
3755
  };
3677
- }, [handleBrowserConfirmation, handleBrowserQuestion]);
3756
+ }, [handleBrowserConfirmation, handleBrowserQuestion, handleBrowserProgress]);
3678
3757
  React3.useEffect(() => {
3679
3758
  if (!isLoadingStyles) {
3680
3759
  onReady?.();
3681
3760
  }
3682
3761
  }, [isLoadingStyles, onReady]);
3762
+ React3.useEffect(() => {
3763
+ if (!getIdentityToken || isLoadingStyles) return;
3764
+ let cancelled = false;
3765
+ const identify = async () => {
3766
+ try {
3767
+ const token = await getIdentityToken();
3768
+ if (!cancelled && token) {
3769
+ window.crow?.("identify", { token });
3770
+ }
3771
+ } catch (e) {
3772
+ console.error("[Crow] getIdentityToken failed:", e);
3773
+ }
3774
+ };
3775
+ identify();
3776
+ const handleRefresh = () => identify();
3777
+ window.addEventListener("crow:token-refresh-needed", handleRefresh);
3778
+ return () => {
3779
+ cancelled = true;
3780
+ window.removeEventListener("crow:token-refresh-needed", handleRefresh);
3781
+ };
3782
+ }, [getIdentityToken, isLoadingStyles]);
3783
+ React3.useEffect(() => {
3784
+ if (typeof window === "undefined") return;
3785
+ if (context && Object.keys(context).length > 0) {
3786
+ window.__crow_page_context = context;
3787
+ } else {
3788
+ window.__crow_page_context = void 0;
3789
+ }
3790
+ }, [context]);
3683
3791
  const handleSend = (message) => {
3684
3792
  if (!message.trim()) return;
3793
+ if (askUserResolver) {
3794
+ chat.addMessage("user", message);
3795
+ askUserResolver.resolve(message);
3796
+ setAskUserResolver(null);
3797
+ setBrowserQuestion(null);
3798
+ return;
3799
+ }
3685
3800
  chat.sendMessage(message);
3686
3801
  };
3802
+ const handleStop = () => {
3803
+ chat.stopGeneration();
3804
+ const currentResolver = askUserResolver;
3805
+ setAskUserResolver(null);
3806
+ setBrowserQuestion(null);
3807
+ if (browserUseEnabled && isBrowserUseActive) {
3808
+ import('@usecrow/client/browser').then(({ stopActiveBrowserUse }) => {
3809
+ stopActiveBrowserUse();
3810
+ currentResolver?.reject(new Error("Stopped by user"));
3811
+ }).catch(() => {
3812
+ currentResolver?.reject(new Error("Stopped by user"));
3813
+ });
3814
+ setIsBrowserUseActive(false);
3815
+ } else if (currentResolver) {
3816
+ currentResolver.reject(new Error("Stopped by user"));
3817
+ }
3818
+ if (pendingConfirmation) {
3819
+ pendingConfirmation.resolve(false);
3820
+ setPendingConfirmation(null);
3821
+ }
3822
+ };
3687
3823
  const handleNewChat = () => {
3688
3824
  if (!isVerifiedUser) {
3689
3825
  setLocalTabMessages((prev) => ({
@@ -4023,13 +4159,14 @@ function CrowCopilot({
4023
4159
  }
4024
4160
  }
4025
4161
  ) }),
4026
- pendingQuestion && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "crow-px-4 crow-py-2", children: /* @__PURE__ */ jsxRuntime.jsx(
4162
+ askUserResolver && browserQuestion && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "crow-px-4 crow-py-2", children: /* @__PURE__ */ jsxRuntime.jsx(
4027
4163
  BrowserUseQuestion,
4028
4164
  {
4029
- question: pendingQuestion.question,
4165
+ question: browserQuestion,
4030
4166
  onSubmit: (answer) => {
4031
- pendingQuestion.resolve(answer);
4032
- setPendingQuestion(null);
4167
+ askUserResolver.resolve(answer);
4168
+ setAskUserResolver(null);
4169
+ setBrowserQuestion(null);
4033
4170
  }
4034
4171
  }
4035
4172
  ) })
@@ -4045,7 +4182,7 @@ function CrowCopilot({
4045
4182
  PromptInputBox,
4046
4183
  {
4047
4184
  onSend: handleSend,
4048
- onStop: chat.stopGeneration,
4185
+ onStop: handleStop,
4049
4186
  placeholder: "Ask anything...",
4050
4187
  isLoading: chat.isLoading
4051
4188
  }