Repository: heroui-inc/heroui
Pull request #4996: feat: open in chat button in doc examples
Author: login: Peterl561
Created: 2025-03-06T17:04:02Z | Merged: 2025-03-10T14:50:50Z

<!---
Thanks for creating a Pull Request ❤️!

Please read the following before submitting:
- PRs that adds new external dependencies might take a while to review.
- Keep your PR as small as possible.
- Limit your PR to one type (docs, feature, refactoring, ci, repo, or bugfix)
-->

Closes # <!-- Github issue # here -->

open in heroui chat button for component code demos
parses dependencies from demo code to pass to api
the error handling toast needs a check for the toast docs page as it already has a provider

## 📝 Description

<!--- Add a brief description -->

## ⛳️ Current behavior (updates)

<!--- Please describe the current behavior that you are modifying -->

## 🚀 New behavior

<!--- Please describe the behavior or changes this PR adds -->

## 💣 Is this a breaking change (Yes/No):

<!-- If Yes, please describe the impact and migration path for existing HeroUI users. -->

## 📝 Additional Information


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
	- Introduced integrated chat functionality across documentation and interactive code demos.
	- Added an “Open in Chat” button that launches a dedicated chat interface with real-time loading indicators and toast notifications.
	- Enhanced the process for gathering necessary code context, ensuring a smoother chat experience.
	- New environment variables for chat features added to configuration.
	- Conditional rendering for the ToastProvider based on document title.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/docs/.env.example b/apps/docs/.env.example
index 3889456f1c..41fa308744 100644
--- a/apps/docs/.env.example
+++ b/apps/docs/.env.example
@@ -21,4 +21,9 @@ NEXT_PUBLIC_FB_FEEDBACK_URL=
 
 # PostHog
 NEXT_PUBLIC_POSTHOG_KEY=your-posthog-key
-NEXT_PUBLIC_POSTHOG_HOST=your-posthog-host
\ No newline at end of file
+NEXT_PUBLIC_POSTHOG_HOST=your-posthog-host
+
+# Chat
+IMPORT_API_KEY=your-import-api-key
+CHAT_API_URL=
+CHAT_URL=
diff --git a/apps/docs/actions/open-in-chat.ts b/apps/docs/actions/open-in-chat.ts
new file mode 100644
index 0000000000..b4374dec6c
--- /dev/null
+++ b/apps/docs/actions/open-in-chat.ts
@@ -0,0 +1,58 @@
+"use server";
+
+import {SandpackFiles} from "@codesandbox/sandpack-react/types";
+
+import {parseDependencies} from "@/components/docs/components/code-demo/parse-dependencies";
+
+const importReact = 'import React from "react";';
+
+export const openInChat = async ({title, files}: {title?: string; files: SandpackFiles}) => {
+  try {
+    // assumes one file for now
+    let content = files["/App.jsx"];
+
+    if (!content || typeof content !== "string") {
+      return {
+        error: "Content is not a string",
+        data: null,
+      };
+    }
+
+    // Check if the file content includes 'React' import statements, if not, add it
+    if (
+      content.includes("React.") &&
+      !content.includes("from 'react'") &&
+      !content.includes('from "react"')
+    ) {
+      content = `${importReact}\n${content}\n`;
+    }
+
+    const dependencies = parseDependencies(content);
+
+    const response = await fetch(`${process.env.CHAT_API_URL}/import`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: `Bearer ${process.env.IMPORT_API_KEY}`,
+      },
+      body: JSON.stringify({
+        title,
+        content,
+        dependencies,
+      }),
+    });
+
+    const result = await response.json();
+
+    if (result.error || !result.path) {
+      return {
+        error: result.error ?? "Unknown error",
+        data: null,
+      };
+    }
+
+    return {error: null, data: `${process.env.CHAT_URL}${result.path}`};
+  } catch (error) {
+    return {error: error, data: null};
+  }
+};
diff --git a/apps/docs/app/docs/[[...slug]]/page.tsx b/apps/docs/app/docs/[[...slug]]/page.tsx
index da649aba04..0d8aa28f70 100644
--- a/apps/docs/app/docs/[[...slug]]/page.tsx
+++ b/apps/docs/app/docs/[[...slug]]/page.tsx
@@ -2,7 +2,7 @@ import type {Metadata} from "next";
 
 import {notFound} from "next/navigation";
 import {allDocs} from "contentlayer2/generated";
-import {Link} from "@heroui/react";
+import {Link, ToastProvider} from "@heroui/react";
 
 import {MDXContent} from "@/components/mdx-content";
 import {siteConfig} from "@/config/site";
@@ -104,6 +104,8 @@ export default async function DocPage({params}: DocPageProps) {
           <DocsToc headings={headings} />
         </div>
       )}
+      {/* toast page has its own provider*/}
+      {doc.title !== "Toast" && <ToastProvider />}
     </>
   );
 }
diff --git a/apps/docs/components/docs/components/code-demo/code-demo.tsx b/apps/docs/components/docs/components/code-demo/code-demo.tsx
index 65b5f9e3e8..d18d4fa991 100644
--- a/apps/docs/components/docs/components/code-demo/code-demo.tsx
+++ b/apps/docs/components/docs/components/code-demo/code-demo.tsx
@@ -1,14 +1,18 @@
 "use client";
 
-import React, {useCallback, useMemo, useRef} from "react";
+import React, {useCallback, useMemo, useRef, useState} from "react";
 import dynamic from "next/dynamic";
-import {Skeleton, Tab, Tabs} from "@heroui/react";
+import {addToast, Button, Skeleton, Spinner, Tab, Tabs} from "@heroui/react";
 import {useInView} from "framer-motion";
+import {usePostHog} from "posthog-js/react";
+import {usePathname} from "next/navigation";
 
 import {useCodeDemo, UseCodeDemoProps} from "./use-code-demo";
 import WindowResizer, {WindowResizerProps} from "./window-resizer";
 
 import {GradientBoxProps} from "@/components/gradient-box";
+import {SmallLogo} from "@/components/heroui-logo";
+import {openInChat} from "@/actions/open-in-chat";
 
 const DynamicReactLiveDemo = dynamic(
   () => import("./react-live-demo").then((m) => m.ReactLiveDemo),
@@ -75,6 +79,11 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
     margin: "600px",
   });
 
+  const pathname = usePathname();
+  const posthog = usePostHog();
+
+  const [isLoading, setIsLoading] = useState(false);
+
   const {noInline, code} = useCodeDemo({
     files,
   });
@@ -166,24 +175,83 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
     return true;
   }, [showTabs, showPreview, showEditor]);
 
+  const isComponentsPage = pathname.includes("/components/");
+
+  const handleOpenInChat = useCallback(async () => {
+    setIsLoading(true);
+
+    const component = pathname.split("/components/")[1];
+
+    posthog.capture("CodeDemo - Open in Chat", {
+      component,
+      demo: title,
+    });
+
+    const capitalizedPath = component.charAt(0).toUpperCase() + component.slice(1);
+    const {data, error} = await openInChat({title: `${capitalizedPath} - ${title}`, files});
+
+    setIsLoading(false);
+
+    if (error || !data) {
+      posthog.capture("CodeDemo - Open in Chat Error", {
+        component,
+        demo: title,
+        error: error ?? "Unknown error",
+      });
+
+      addToast({
+        title: "Error",
+        description: error ?? "Unknown error",
+        color: "danger",
+      });
+
+      return;
+    }
+
+    window.open(data, "_blank");
+  }, [pathname, title, files, posthog]);
+
   return (
-    <div ref={ref} className="flex flex-col gap-2">
+    <div ref={ref} className="flex flex-col gap-2 relative">
       {shouldRenderTabs ? (
-        <Tabs
-          disableAnimation
-          aria-label="Code demo tabs"
-          classNames={{
-            panel: "pt-0",
-          }}
-          variant="underlined"
-        >
-          <Tab key="preview" title="Preview">
-            {previewContent}
-          </Tab>
-          <Tab key="code" title="Code">
-            {editorContent}
-          </Tab>
-        </Tabs>
+        <>
+          <Tabs
+            disableAnimation
+            aria-label="Code demo tabs"
+            classNames={{
+              panel: "pt-0",
+            }}
+            variant="underlined"
+          >
+            <Tab key="preview" title="Preview">
+              {previewContent}
+            </Tab>
+            <Tab key="code" title="Code">
+              {editorContent}
+            </Tab>
+          </Tabs>
+          {isComponentsPage && (
+            <Button
+              className="absolute right-1 top-1 border-1"
+              isDisabled={isLoading}
+              size="sm"
+              variant="bordered"
+              onPress={handleOpenInChat}
+            >
+              Open in Chat{" "}
+              {isLoading ? (
+                <Spinner
+                  classNames={{wrapper: "h-4 w-4"}}
+                  color="current"
+                  size="sm"
+                  variant="simple"
+                />
+              ) : (
+                <SmallLogo className="w-4 h-4" />
+              )}
+            </Button>
+          )}
+        </>
       ) : (
         <>
           {previewContent}
diff --git a/apps/docs/components/docs/components/code-demo/parse-dependencies.ts b/apps/docs/components/docs/components/code-demo/parse-dependencies.ts
new file mode 100644
index 0000000000..cfcb10b5a3
--- /dev/null
+++ b/apps/docs/components/docs/components/code-demo/parse-dependencies.ts
@@ -0,0 +1,33 @@
+const packageRegex = /(?:from|import)\s+(?:.*\s+from\s+)?['"]([^'"]+)['"]/g;
+
+export const parseDependencies = (content: string) => {
+  const dependencies: {name: string; version: string}[] = [];
+
+  content.match(packageRegex)?.forEach((match) => {
+    if (match.includes("@heroui")) {
+      return;
+    }
+
+    if (match.includes("./") || match.includes("../")) {
+      return;
+    }
+
+    const packageName = match.match(/['"]([^'"]+)['"]/)?.[1];
+
+    if (!packageName) {
+      return;
+    }
+
+    dependencies.push({
+      name: packageName,
+      version: fixedVersions[packageName] || "latest",
+    });
+  });
+
+  return dependencies;
+};
+
+const fixedVersions = {
+  "@internationalized/date": "3.7.0",
+  "@react-aria/i18n": "3.12.5",
+};


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>apps/docs/components/docs/components/code-demo/parse-dependencies.ts (2)</summary><blockquote>

`1-2`: **Consider adding more robust package detection**

The regex pattern looks good for basic import/from statements but might miss complex imports or dynamic imports.

Consider enhancing the regex to handle more edge cases or adding a comment about its limitations. For complex projects, a more robust parsing approach might be needed in the future.

---

`30-33`: **Consider making fixedVersions more maintainable**

The hardcoded version mapping works but could become difficult to maintain as dependencies grow.

Consider moving this to a separate configuration file that can be more easily maintained, possibly with comments explaining why specific versions are required.

</blockquote></details>
<details>
<summary>apps/docs/actions/open-in-chat.ts (1)</summary><blockquote>

`9-19`: **Add type declaration for return value**

The function's return type should be explicitly defined for better type safety and documentation.

```diff
- export const openInChat = async ({title, files}: {title?: string; files: SandpackFiles}) => {
+ export const openInChat = async ({title, files}: {title?: string; files: SandpackFiles}): Promise<{error: string | null; data: string | null}> => {
```

</blockquote></details>
<details>
<summary>apps/docs/components/docs/components/code-demo/code-demo.tsx (2)</summary><blockquote>

`178-178`: **Consider a more robust path check.**

Using `pathname.includes("/components/")` works but could potentially catch false positives if there are other paths containing this string.

```diff
-  const isComponentsPage = pathname.includes("/components/");
+  const isComponentsPage = /^\/components\/[^\/]+/.test(pathname);
```

---

`180-212`: **Well-implemented chat integration with analytics tracking.**

The implementation handles the complete flow well: capturing analytics events, managing loading state, handling errors, and opening the chat in a new window.

A few suggestions for improvement:


1. The component name extraction logic could be more robust for handling kebab-case paths:

```diff
-  const component = pathname.split("/components/")[1];
+  // Handle paths with trailing slash and extract just the component segment
+  const componentSegment = pathname.split("/components/")[1]?.split("/")[0] || "";
+  const component = componentSegment;

-  const capitalizedPath = component.charAt(0).toUpperCase() + component.slice(1);
+  // Convert kebab-case to Title Case (e.g., "button-group" → "Button Group")
+  const capitalizedPath = component
+    .split("-")
+    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
+    .join(" ");
```

2. Consider adding more specific error messages when possible instead of just displaying the generic error:

```diff
   addToast({
     title: "Error",
-    description: error ?? "Unknown error",
+    description: error ? `Failed to open chat: ${error}` : "Failed to open chat due to an unknown error",
     color: "danger",
   });
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9a95a71aef19afed021f8891e052be0f1996dc25 and e96348064531393960270afce3d5ea64ef81cc33.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `apps/docs/.env.example` (1 hunks)
* `apps/docs/actions/open-in-chat.ts` (1 hunks)
* `apps/docs/app/docs/[[...slug]]/page.tsx` (2 hunks)
* `apps/docs/components/docs/components/code-demo/code-demo.tsx` (3 hunks)
* `apps/docs/components/docs/components/code-demo/parse-dependencies.ts` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (9)</summary><blockquote>

<details>
<summary>apps/docs/app/docs/[[...slug]]/page.tsx (2)</summary>

`5-5`: **Import addition looks good**

The ToastProvider import from @heroui/react has been correctly added to support the new chat feature's error notifications.

---

`107-108`: **Conditional ToastProvider rendering implemented correctly**

Good implementation to avoid duplicate ToastProvider on the Toast documentation page. The conditional check and comment provide clear context for why this approach was taken.

</details>
<details>
<summary>apps/docs/.env.example (1)</summary>

`24-29`: **Environment variables added correctly for chat feature**

The new environment variables for the chat feature have been properly documented in the .env.example file with a clear section header. This will help other developers understand what environment variables they need to set up.

</details>
<details>
<summary>apps/docs/components/docs/components/code-demo/parse-dependencies.ts (1)</summary>

`3-28`: **Dependencies parsing logic looks solid**

The function correctly filters out internal (@heroui) and relative imports while constructing a dependencies array with appropriate versions.


One minor suggestion: Consider adding type annotations for better clarity and documentation:
```diff
- export const parseDependencies = (content: string) => {
+ export const parseDependencies = (content: string): {name: string; version: string}[] => {
```

</details>
<details>
<summary>apps/docs/actions/open-in-chat.ts (2)</summary>

`21-28`: **React import check logic is correct**

Good approach to ensure React is imported when React.* syntax is used in the code.

---

`47-54`: **Validate environment variables**

The code assumes process.env.CHAT_URL will be defined, but doesn't check for it.

Add a check to ensure environment variables are defined:

```diff
+    if (!process.env.CHAT_URL) {
+      return {
+        error: "CHAT_URL environment variable is not defined",
+        data: null,
+      };
+    }
+
     return {error: null, data: `${process.env.CHAT_URL}${result.path}`};
```

</details>
<details>
<summary>apps/docs/components/docs/components/code-demo/code-demo.tsx (3)</summary>

`3-8`: **Well-structured imports, good organization.**

The new imports are organized logically, bringing in all the necessary functionality for the chat feature while maintaining clean code structure.

---

`83-85`: **Good setup of state and hooks.**

Proper use of PostHog for analytics tracking and useState for managing the loading state of the chat button.

---

`233-253`: **Good UI implementation of the chat button.**

The button is nicely integrated with loading state indicators. The conditional rendering based on the page type is appropriate.


One consideration:

The absolute positioning (`absolute right-0 top-1`) might overlap with other elements depending on the layout. Verify that this positioning works well across different viewport sizes and doesn't interfere with other UI elements in the header area.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T17:09:47Z
user: login: jrgarciadev
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-10T14:50:38Z

Review comments:
diff_hunk: @@ -0,0 +1,58 @@
+"use server";
+
+import {SandpackFiles} from "@codesandbox/sandpack-react/types";
+
+import {parseDependencies} from "@/components/docs/components/code-demo/parse-dependencies";
+
+const importReact = 'import React from "react";';
+
+export const openInChat = async ({title, files}: {title?: string; files: SandpackFiles}) => {
+  try {
+    // assumes one file for now
+    let content = files["/App.jsx"];
+
+    if (!content || typeof content !== "string") {
+      return {
+        error: "Content is not a string",
+        data: null,
+      };
+    }
+
+    // Check if the file content includes 'React' import statements, if not, add it
+    if (
+      content.includes("React.") &&
+      !content.includes("from 'react'") &&
+      !content.includes('from "react"')
+    ) {
+      content = `${importReact}\n${content}\n`;
+    }
+
+    const dependencies = parseDependencies(content);
+
+    const response = await fetch(`${process.env.CHAT_API_URL}/import`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: `Bearer ${process.env.IMPORT_API_KEY}`,
+      },
+      body: JSON.stringify({
+        title,
+        content,
+        dependencies,
+      }),
+    });
+
+    const result = await response.json();
+
+    if (result.error || !result.path) {
+      return {
+        error: result.error ?? "Unknown error",
+        data: null,
+      };
+    }
+
+    return {error: null, data: `${process.env.CHAT_URL}${result.path}`};
+  } catch (error) {
+    return {error: error, data: null};
+  }
path: apps/docs/actions/open-in-chat.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Improve error handling in catch block**

The error message may not be a string, which could cause issues when returned.

```diff
-    return {error: error, data: null};
+    const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
+    console.error("Error in openInChat:", error);
+    return {error: errorMessage, data: null};
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
    console.error("Error in openInChat:", error);
    return {error: errorMessage, data: null};
  }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-07T17:09:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,58 @@
+"use server";
+
+import {SandpackFiles} from "@codesandbox/sandpack-react/types";
+
+import {parseDependencies} from "@/components/docs/components/code-demo/parse-dependencies";
+
+const importReact = 'import React from "react";';
+
+export const openInChat = async ({title, files}: {title?: string; files: SandpackFiles}) => {
+  try {
+    // assumes one file for now
+    let content = files["/App.jsx"];
+
+    if (!content || typeof content !== "string") {
+      return {
+        error: "Content is not a string",
+        data: null,
+      };
+    }
+
+    // Check if the file content includes 'React' import statements, if not, add it
+    if (
+      content.includes("React.") &&
+      !content.includes("from 'react'") &&
+      !content.includes('from "react"')
+    ) {
+      content = `${importReact}\n${content}\n`;
+    }
+
+    const dependencies = parseDependencies(content);
+
+    const response = await fetch(`${process.env.CHAT_API_URL}/import`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+        Authorization: `Bearer ${process.env.IMPORT_API_KEY}`,
+      },
+      body: JSON.stringify({
+        title,
+        content,
+        dependencies,
+      }),
+    });
path: apps/docs/actions/open-in-chat.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add error handling for API request**

The fetch request lacks timeout and network error handling.

```diff
-    const response = await fetch(`${process.env.CHAT_API_URL}/import`, {
+    const controller = new AbortController();
+    const timeoutId = setTimeout(() => controller.abort(), 10000); // 10-second timeout
+
+    const response = await fetch(`${process.env.CHAT_API_URL}/import`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.IMPORT_API_KEY}`,
      },
+     signal: controller.signal,
      body: JSON.stringify({
        title,
        content,
        dependencies,
      }),
    });
+
+    clearTimeout(timeoutId);
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000); // 10-second timeout

    const response = await fetch(`${process.env.CHAT_API_URL}/import`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.IMPORT_API_KEY}`,
      },
      signal: controller.signal,
      body: JSON.stringify({
        title,
        content,
        dependencies,
      }),
    });

    clearTimeout(timeoutId);
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-07T17:09:46Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-03-06T17:04:06Z
body: ###  ⚠️  No Changeset found

Latest commit: 869ccb1c3745edd0befad1b10b6fba66c90b9f9d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/heroui-inc/heroui/new/feat/open-in-chat?filename=.changeset/large-ties-smash.md&value=---%0A%22%40heroui%2Fdocs%22%3A%20patch%0A---%0A%0Afeat%3A%20open%20in%20chat%20button%20in%20doc%20examples%0A)


author_association: NONE
user: login: vercel[bot]
created_at: 2025-03-06T17:04:07Z
body: [vc]: #o0FpdtHRtRqn5MO7Ydgrd/PbsvcbBTU91PQ7vk8Jils=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJoZXJvdWktc2IiLCJyb290RGlyZWN0b3J5IjoicGFja2FnZXMvc3Rvcnlib29rIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2hlcm91aS9oZXJvdWktc2IvSjhGdE5jZDZVV3lFODhlaHJIclN6aHlXVXdlYSIsInByZXZpZXdVcmwiOiJoZXJvdWktc2ItZ2l0LWZlYXQtb3Blbi1pbi1jaGF0LWhlcm91aS52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6Imhlcm91aS1zYi1naXQtZmVhdC1vcGVuLWluLWNoYXQtaGVyb3VpLnZlcmNlbC5hcHAifX0seyJuYW1lIjoiaGVyb3VpIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvZG9jcyIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9oZXJvdWkvaGVyb3VpLzVIQkw5elUxeGZ2cUM4dncxaWdOTjNqOHJhSGEiLCJwcmV2aWV3VXJsIjoiaGVyb3VpLWdpdC1mZWF0LW9wZW4taW4tY2hhdC1oZXJvdWkudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJoZXJvdWktZ2l0LWZlYXQtb3Blbi1pbi1jaGF0LWhlcm91aS52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **heroui** | ✅ Ready ([Inspect](https://vercel.com/heroui/heroui/5HBL9zU1xfvqC8vw1igNN3j8raHa)) | [Visit Preview](https://heroui-git-feat-open-in-chat-heroui.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/heroui-git-feat-open-in-chat-heroui.vercel.app?via=pr-comment-feedback-link) | Mar 10, 2025 2:49pm |
| **heroui-sb** | ✅ Ready ([Inspect](https://vercel.com/heroui/heroui-sb/J8FtNcd6UWyE88ehrHrSzhyWUwea)) | [Visit Preview](https://heroui-sb-git-feat-open-in-chat-heroui.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/heroui-sb-git-feat-open-in-chat-heroui.vercel.app?via=pr-comment-feedback-link) | Mar 10, 2025 2:49pm |



author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-03-06T17:04:10Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request integrates a new chat feature into the docs app. New environment variables are added to configure the chat API. An asynchronous function (`openInChat`) is implemented to retrieve file content, ensure proper React import, parse dependencies, and make a POST request to the chat API. The chat integration is further supported by updates in the `CodeDemo` component to handle button actions, loading states, and error tracking. Additionally, the documentation page now conditionally renders the `ToastProvider` based on the document title.

## Changes

| File(s) | Change Summary |
| --- | --- |
| `apps/docs/.env.example` | Added new environment variables: `IMPORT_API_KEY`, `CHAT_API_URL`, and `CHAT_URL` under a new `# Chat` section. |
| `apps/docs/actions/open-in-chat.ts` | Implemented the asynchronous `openInChat` function to process file content, validate React imports, and make a POST request to the chat API. |
| `apps/docs/components/docs/components/code-demo/code-demo.tsx` | Enhanced the `CodeDemo` component with a new `handleOpenInChat` method to trigger the chat process, manage loading states, and capture errors. |
| `apps/docs/components/docs/components/code-demo/parse-dependencies.ts` | Introduced the `parseDependencies` function to extract package dependencies from a string input. |
| `apps/docs/app/docs/[[...slug]]/page.tsx` | Added conditional import and rendering of `ToastProvider` from `@heroui/react` in the `DocPage` component based on the document title. |

## Suggested labels
`📋 Scope : Docs`

## Suggested reviewers
- wingkwong

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
>  ERR_PNPM_OPTIONAL_DEPS_REQUIRE_PROD_DEPS  Optional dependencies cannot be installed without production dependencies
> 
> 
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e96348064531393960270afce3d5ea64ef81cc33 and 869ccb1c3745edd0befad1b10b6fba66c90b9f9d.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `apps/docs/components/docs/components/code-demo/code-demo.tsx` (3 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* apps/docs/components/docs/components/code-demo/code-demo.tsx

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: TypeScript
* GitHub Check: Tests
* GitHub Check: Prettier
* GitHub Check: Build
* GitHub Check: Continuous Release

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=heroui-inc/heroui&utm_content=4996):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: pkg-pr-new[bot]
created_at: 2025-03-06T17:12:49Z
body: 
[Open in Stackblitz](https://pkg.pr.new/template/cfff5068-48f4-4b54-91db-0020589ae768)


<details><summary><b>@heroui/accordion</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/accordion@4996
```

</p></details>
      
    

<details><summary><b>@heroui/autocomplete</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/autocomplete@4996
```

</p></details>
      
    

<details><summary><b>@heroui/alert</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/alert@4996
```

</p></details>
      
    

<details><summary><b>@heroui/avatar</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/avatar@4996
```

</p></details>
      
    

<details><summary><b>@heroui/badge</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/badge@4996
```

</p></details>
      
    

<details><summary><b>@heroui/breadcrumbs</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/breadcrumbs@4996
```

</p></details>
      
    

<details><summary><b>@heroui/button</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/button@4996
```

</p></details>
      
    

<details><summary><b>@heroui/calendar</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/calendar@4996
```

</p></details>
      
    

<details><summary><b>@heroui/card</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/card@4996
```

</p></details>
      
    

<details><summary><b>@heroui/checkbox</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/checkbox@4996
```

</p></details>
      
    

<details><summary><b>@heroui/chip</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/chip@4996
```

</p></details>
      
    

<details><summary><b>@heroui/code</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/code@4996
```

</p></details>
      
    

<details><summary><b>@heroui/date-input</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/date-input@4996
```

</p></details>
      
    

<details><summary><b>@heroui/date-picker</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/date-picker@4996
```

</p></details>
      
    

<details><summary><b>@heroui/divider</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/divider@4996
```

</p></details>
      
    

<details><summary><b>@heroui/drawer</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/drawer@4996
```

</p></details>
      
    

<details><summary><b>@heroui/dropdown</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/dropdown@4996
```

</p></details>
      
    

<details><summary><b>@heroui/form</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/form@4996
```

</p></details>
      
    

<details><summary><b>@heroui/image</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/image@4996
```

</p></details>
      
    

<details><summary><b>@heroui/input</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/input@4996
```

</p></details>
      
    

<details><summary><b>@heroui/input-otp</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/input-otp@4996
```

</p></details>
      
    

<details><summary><b>@heroui/kbd</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/kbd@4996
```

</p></details>
      
    

<details><summary><b>@heroui/link</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/link@4996
```

</p></details>
      
    

<details><summary><b>@heroui/listbox</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/listbox@4996
```

</p></details>
      
    

<details><summary><b>@heroui/menu</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/menu@4996
```

</p></details>
      
    

<details><summary><b>@heroui/modal</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/modal@4996
```

</p></details>
      
    

<details><summary><b>@heroui/navbar</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/navbar@4996
```

</p></details>
      
    

<details><summary><b>@heroui/pagination</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/pagination@4996
```

</p></details>
      
    

<details><summary><b>@heroui/number-input</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/number-input@4996
```

</p></details>
      
    

<details><summary><b>@heroui/popover</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/popover@4996
```

</p></details>
      
    

<details><summary><b>@heroui/progress</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/progress@4996
```

</p></details>
      
    

<details><summary><b>@heroui/radio</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/radio@4996
```

</p></details>
      
    

<details><summary><b>@heroui/ripple</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/ripple@4996
```

</p></details>
      
    

<details><summary><b>@heroui/scroll-shadow</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/scroll-shadow@4996
```

</p></details>
      
    

<details><summary><b>@heroui/select</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/select@4996
```

</p></details>
      
    

<details><summary><b>@heroui/skeleton</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/skeleton@4996
```

</p></details>
      
    

<details><summary><b>@heroui/slider</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/slider@4996
```

</p></details>
      
    

<details><summary><b>@heroui/snippet</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/snippet@4996
```

</p></details>
      
    

<details><summary><b>@heroui/spacer</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/spacer@4996
```

</p></details>
      
    

<details><summary><b>@heroui/spinner</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/spinner@4996
```

</p></details>
      
    

<details><summary><b>@heroui/switch</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/switch@4996
```

</p></details>
      
    

<details><summary><b>@heroui/table</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/table@4996
```

</p></details>
      
    

<details><summary><b>@heroui/tabs</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/tabs@4996
```

</p></details>
      
    

<details><summary><b>@heroui/toast</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/toast@4996
```

</p></details>
      
    

<details><summary><b>@heroui/tooltip</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/tooltip@4996
```

</p></details>
      
    

<details><summary><b>@heroui/user</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/user@4996
```

</p></details>
      
    

<details><summary><b>@heroui/react</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/react@4996
```

</p></details>
      
    

<details><summary><b>@heroui/system</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/system@4996
```

</p></details>
      
    

<details><summary><b>@heroui/system-rsc</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/system-rsc@4996
```

</p></details>
      
    

<details><summary><b>@heroui/theme</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/theme@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-aria-accordion</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-aria-accordion@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-aria-accordion-item</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-aria-accordion-item@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-aria-button</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-aria-button@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-aria-link</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-aria-link@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-aria-modal-overlay</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-aria-modal-overlay@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-aria-multiselect</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-aria-multiselect@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-callback-ref</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-callback-ref@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-clipboard</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-clipboard@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-data-scroll-overflow</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-data-scroll-overflow@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-disclosure</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-disclosure@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-draggable</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-draggable@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-infinite-scroll</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-infinite-scroll@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-image</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-image@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-intersection-observer</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-intersection-observer@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-is-mobile</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-is-mobile@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-is-mounted</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-is-mounted@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-measure</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-measure@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-pagination</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-pagination@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-real-shape</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-real-shape@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-ref-state</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-ref-state@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-resize</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-resize@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-scroll-position</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-scroll-position@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-safe-layout-effect</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-safe-layout-effect@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-ssr</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-ssr@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-theme</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-theme@4996
```

</p></details>
      
    

<details><summary><b>@heroui/use-update-effect</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/use-update-effect@4996
```

</p></details>
      
    

<details><summary><b>@heroui/aria-utils</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/aria-utils@4996
```

</p></details>
      
    

<details><summary><b>@heroui/dom-animation</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/dom-animation@4996
```

</p></details>
      
    

<details><summary><b>@heroui/framer-utils</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/framer-utils@4996
```

</p></details>
      
    

<details><summary><b>@heroui/react-rsc-utils</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/react-rsc-utils@4996
```

</p></details>
      
    

<details><summary><b>@heroui/react-utils</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/react-utils@4996
```

</p></details>
      
    

<details><summary><b>@heroui/shared-icons</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/shared-icons@4996
```

</p></details>
      
    

<details><summary><b>@heroui/shared-utils</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/shared-utils@4996
```

</p></details>
      
    

<details><summary><b>@heroui/stories-utils</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/stories-utils@4996
```

</p></details>
      
    

<details><summary><b>@heroui/test-utils</b></summary><p>

```
npm i https://pkg.pr.new/@heroui/test-utils@4996
```

</p></details>
      
    

_commit: <a href="https://github.com/heroui-inc/heroui/runs/38498015004"><code>869ccb1</code></a>_

author_association: NONE