chore(docs): remove chat linkage (#5959)

* chore: remove unused globalEnv

* chore(docs): remove chat links
This commit is contained in:
WK 2025-12-05 03:59:57 +08:00 committed by GitHub
parent 4e3098a512
commit a7655ad2c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 3 additions and 317 deletions

View File

@ -22,8 +22,3 @@ NEXT_PUBLIC_FB_FEEDBACK_URL=
# PostHog
NEXT_PUBLIC_POSTHOG_KEY=your-posthog-key
NEXT_PUBLIC_POSTHOG_HOST=your-posthog-host
# Chat
IMPORT_API_KEY=your-import-api-key
CHAT_API_URL=
CHAT_URL=

View File

@ -1,100 +0,0 @@
"use server";
import {toKebabCase, toPascalCase} from "@/components/docs/components/code-demo/utils";
const importReact = 'import React from "react";';
export const openInChat = async ({
component,
title,
content,
dependencies,
useWrapper,
}: {
component: string;
title?: string;
content: string;
dependencies: {name: string; version: string}[];
useWrapper: boolean;
}) => {
try {
// 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`;
}
let files: Record<string, string> = {
"src/App.tsx": content,
};
const fullName = `${component.charAt(0).toUpperCase() + component.slice(1)} - ${title}`;
if (useWrapper) {
files = getFilesWithWrapper(fullName, 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: `${component.charAt(0).toUpperCase() + component.slice(1)} - ${title}`,
files,
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
}&utm_source=heroui.com&utm_medium=open-in-chat&utm_content=${encodeURIComponent(
title ?? "unknown",
)}`,
};
} catch (error) {
return {error: error, data: null};
}
};
const getFilesWithWrapper = (name: string, content: string) => {
const pascalName = toPascalCase(name);
const kebabName = toKebabCase(name);
// Replace the export default function name
const updatedContent = content.replace(
"export default function App()",
`export default function ${pascalName}()`,
);
const wrapperContent = `import ${pascalName} from "./components/${kebabName}";
export default function App() {
return (
<div className="flex min-h-screen items-center justify-center p-6">
<${pascalName} />
</div>
);
}
`;
return {
[`src/components/${kebabName}.tsx`]: updatedContent,
[`src/App.tsx`]: wrapperContent,
};
};

View File

@ -4,18 +4,13 @@ import type {UseCodeDemoProps} from "./use-code-demo";
import type {WindowResizerProps} from "./window-resizer";
import type {GradientBoxProps} from "@/components/gradient-box";
import React, {useCallback, useMemo, useRef, useState} from "react";
import React, {useCallback, useMemo, useRef} from "react";
import dynamic from "next/dynamic";
import {addToast, Button, Skeleton, Spinner, Tab, Tabs} from "@heroui/react";
import {Skeleton, Tab, Tabs} from "@heroui/react";
import {useInView} from "framer-motion";
import {usePostHog} from "posthog-js/react";
import {usePathname} from "next/navigation";
import {useCodeDemo} from "./use-code-demo";
import WindowResizer from "./window-resizer";
import {parseDependencies} from "./parse-dependencies";
import {openInChat} from "@/actions/open-in-chat";
const DynamicReactLiveDemo = dynamic(
() => import("./react-live-demo").then((m) => m.ReactLiveDemo),
@ -82,11 +77,6 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
margin: "600px",
});
const pathname = usePathname();
const posthog = usePostHog();
const [isLoading, setIsLoading] = useState(false);
const {noInline, code} = useCodeDemo({
files,
});
@ -178,64 +168,6 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
return true;
}, [showTabs, showPreview, showEditor]);
const isComponentsPage = pathname.includes("/components/");
const handleOpenInChat = useCallback(async () => {
setIsLoading(true);
// assume doc demo files are all App.jsx
const content = files["/App.jsx"];
if (!content || typeof content !== "string") {
addToast({
title: "Error",
description: "Invalid demo content",
color: "danger",
});
return;
}
const component = pathname.split("/components/")[1];
const dependencies = parseDependencies(content);
posthog.capture("CodeDemo - Open in Chat", {
component,
demo: title,
});
const newTab = window.open(undefined, "_blank");
const {data, error} = await openInChat({
component,
title,
content,
dependencies,
useWrapper: !asIframe,
});
setIsLoading(false);
if (error || !data) {
if (newTab) newTab.close();
posthog.capture("CodeDemo - Open in Chat Error", {
component,
demo: title,
error: error ?? "Unknown error",
});
addToast({
title: "Error",
description: error ?? "Unknown error",
color: "danger",
});
return;
}
if (newTab) newTab.location.href = data;
}, [pathname, title, files, posthog]);
return (
<div ref={ref} className="flex flex-col gap-2 relative">
{shouldRenderTabs ? (
@ -255,26 +187,6 @@ export const CodeDemo: React.FC<CodeDemoProps> = ({
{editorContent}
</Tab>
</Tabs>
{isComponentsPage && (
<Button
disableRipple
className="absolute rounded-[9px] right-1 top-1 border-1 border-default-200 dark:border-default-100 data-[hover=true]:bg-default-50/80"
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"
/>
) : null}
</Button>
)}
</>
) : (
<>

View File

@ -1,92 +0,0 @@
"use client";
import {Icon} from "@iconify/react/dist/offline";
import arrowRightIcon from "@iconify/icons-solar/arrow-right-linear";
import {usePathname} from "next/navigation";
import {useEffect} from "react";
import {usePostHog} from "posthog-js/react";
import emitter from "@/libs/emitter";
const hideOnPaths = ["examples"];
export const HeroUIChatBanner = () => {
const posthog = usePostHog();
const handleClick = () => {
posthog.capture("HeroUI Chat Banner", {
action: "click",
category: "landing-page",
});
};
const pathname = usePathname();
const shouldBeVisible = !hideOnPaths.some((path) => pathname.includes(path));
useEffect(() => {
if (!shouldBeVisible) return;
// listen to scroll event, dispatch an event when scroll is at the top < 48 px
const handleScroll = () => {
if (window.scrollY < 48) {
emitter.emit("proBannerVisibilityChange", "visible");
} else {
emitter.emit("proBannerVisibilityChange", "hidden");
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [shouldBeVisible]);
if (!shouldBeVisible) return null;
return (
<div className="relative z-50 isolate flex items-center gap-x-6 overflow-hidden bg-background border-b-1 border-divider px-6 py-2 sm:px-3.5 sm:before:flex-1">
<div className="flex w-full items-center justify-between md:justify-center gap-x-3">
<a
className="text-small flex items-end sm:text-[0.93rem] text-foreground hover:opacity-80 transition-opacity"
href="https://heroui.chat?utm_source=heroui.com&utm_medium=top-banner"
rel="noopener noreferrer"
target="_blank"
onClick={handleClick}
>
<span aria-label="rocket" className="hidden md:block" role="img">
🚀
</span>
<span
className="inline-flex md:ml-1 animate-text-gradient font-medium bg-clip-text text-transparent bg-[linear-gradient(90deg,#27272A_0%,#52525B_50%,#52525B_100%)] dark:bg-[linear-gradient(90deg,#E5E5E5_0%,#A1A1AA_50%,#E5E5E5_100%)]"
style={{
fontSize: "inherit",
backgroundSize: "200%",
backgroundClip: "text",
WebkitBackgroundClip: "text",
color: "transparent",
}}
>
Generate, edit and deploy beautiful apps
</span>
</a>
<a
className="flex group min-w-[120px] items-center font-semibold text-background bg-foreground shadow-sm gap-1.5 relative overflow-hidden rounded-full p-[1px] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
href="https://heroui.chat?utm_source=heroui.com&utm_medium=top-banner"
rel="noopener noreferrer"
onClick={handleClick}
>
<div className="inline-flex h-full w-full cursor-pointer items-center justify-center rounded-full bg-foreground group-hover:bg-foreground/70 transition-background px-3 py-1 text-sm font-medium text-background">
HeroUI Chat
<Icon
aria-hidden="true"
className="outline-solid outline-transparent transition-transform group-hover:translate-x-0.5 [&>path]:stroke-[2px]"
icon={arrowRightIcon}
width={16}
/>
</div>
</a>
</div>
</div>
);
};

View File

@ -1,26 +0,0 @@
"use client";
import {useState, useEffect} from "react";
import {HeroUIChatBanner} from "@/components/heroui-chat-banner";
import {ProBanner} from "@/components/pro-banner";
export const RandomBanner = () => {
const [showChatBanner, setShowChatBanner] = useState<boolean | null>(null);
useEffect(() => {
const bannerCount = parseInt(sessionStorage.getItem("bannerCount") || "0", 10);
const shouldShowChat = bannerCount % 2 === 0;
setShowChatBanner(shouldShowChat);
sessionStorage.setItem("bannerCount", String(bannerCount + 1));
}, []);
if (showChatBanner === null) {
return <div className="h-[47px] border-b border-divider" />;
}
return showChatBanner ? <HeroUIChatBanner /> : <ProBanner />;
};

View File

@ -6,10 +6,7 @@
"IS_PREVIEW",
"IS_VA_ENABLED",
"ENABLE_EXPERIMENTAL_COREPACK",
"PLAIN_USER_AUTHENTICATED",
"IMPORT_API_KEY",
"CHAT_URL",
"CHAT_API_URL"
"PLAIN_USER_AUTHENTICATED"
],
"tasks": {
"build": {