feat(cli): add streamdown in ai example of all react web templates (#554)

This commit is contained in:
Aman Varshney
2025-08-30 22:08:22 +05:30
committed by GitHub
parent 383ea6ff33
commit f8684863a6
9 changed files with 51 additions and 24 deletions

View File

@@ -108,6 +108,7 @@ export const dependencyVersionMap = {
"@ai-sdk/vue": "^2.0.9", "@ai-sdk/vue": "^2.0.9",
"@ai-sdk/svelte": "^3.0.9", "@ai-sdk/svelte": "^3.0.9",
"@ai-sdk/react": "^2.0.9", "@ai-sdk/react": "^2.0.9",
streamdown: "^1.1.6",
"@orpc/server": "^1.8.4", "@orpc/server": "^1.8.4",
"@orpc/client": "^1.8.4", "@orpc/client": "^1.8.4",

View File

@@ -43,7 +43,7 @@ export async function setupExamples(config: ProjectConfig) {
} else if (hasSvelte) { } else if (hasSvelte) {
dependencies.push("@ai-sdk/svelte"); dependencies.push("@ai-sdk/svelte");
} else if (hasReactWeb) { } else if (hasReactWeb) {
dependencies.push("@ai-sdk/react"); dependencies.push("@ai-sdk/react", "streamdown");
} }
await addPackageDependency({ await addPackageDependency({
dependencies, dependencies,

View File

@@ -740,6 +740,19 @@ export async function setupExamplesTemplate(
if (hasReactWeb) { if (hasReactWeb) {
const exampleWebSrc = path.join(exampleBaseDir, "web/react"); const exampleWebSrc = path.join(exampleBaseDir, "web/react");
if (await fs.pathExists(exampleWebSrc)) { if (await fs.pathExists(exampleWebSrc)) {
if (example === "ai") {
const exampleWebBaseSrc = path.join(exampleWebSrc, "base");
if (await fs.pathExists(exampleWebBaseSrc)) {
await processAndCopyFiles(
"**/*",
exampleWebBaseSrc,
webAppDir,
context,
false,
);
}
}
const reactFramework = context.frontend.find((f) => const reactFramework = context.frontend.find((f) =>
[ [
"next", "next",

View File

@@ -0,0 +1,22 @@
"use client";
import { type ComponentProps, memo } from "react";
import { Streamdown } from "streamdown";
import { cn } from "@/lib/utils";
type ResponseProps = ComponentProps<typeof Streamdown>;
export const Response = memo(
({ className, ...props }: ResponseProps) => (
<Streamdown
className={cn(
"size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
className,
)}
{...props}
/>
),
(prevProps, nextProps) => prevProps.children === nextProps.children,
);
Response.displayName = "Response";

View File

@@ -2,10 +2,11 @@
import { useChat } from "@ai-sdk/react"; import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai"; import { DefaultChatTransport } from "ai";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Send } from "lucide-react"; import { Send } from "lucide-react";
import { useRef, useEffect, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { Response } from "@/components/response";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
export default function AIPage() { export default function AIPage() {
const [input, setInput] = useState(""); const [input, setInput] = useState("");
@@ -51,11 +52,7 @@ export default function AIPage() {
</p> </p>
{message.parts?.map((part, index) => { {message.parts?.map((part, index) => {
if (part.type === "text") { if (part.type === "text") {
return ( return <Response key={index}>{part.text}</Response>;
<div key={index} className="whitespace-pre-wrap">
{part.text}
</div>
);
} }
return null; return null;
})} })}

View File

@@ -4,6 +4,7 @@ import { DefaultChatTransport } from "ai";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Send } from "lucide-react"; import { Send } from "lucide-react";
import { Response } from "@/components/response";
const AI: React.FC = () => { const AI: React.FC = () => {
const [input, setInput] = useState(""); const [input, setInput] = useState("");
@@ -49,11 +50,7 @@ const AI: React.FC = () => {
</p> </p>
{message.parts?.map((part, index) => { {message.parts?.map((part, index) => {
if (part.type === "text") { if (part.type === "text") {
return ( return <Response key={index}>{part.text}</Response>;
<div key={index} className="whitespace-pre-wrap">
{part.text}
</div>
);
} }
return null; return null;
})} })}

View File

@@ -5,6 +5,7 @@ import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Send } from "lucide-react"; import { Send } from "lucide-react";
import { useRef, useEffect, useState } from "react"; import { useRef, useEffect, useState } from "react";
import { Response } from "@/components/response";
export const Route = createFileRoute("/ai")({ export const Route = createFileRoute("/ai")({
component: RouteComponent, component: RouteComponent,
@@ -54,11 +55,7 @@ function RouteComponent() {
</p> </p>
{message.parts?.map((part, index) => { {message.parts?.map((part, index) => {
if (part.type === "text") { if (part.type === "text") {
return ( return <Response key={index}>{part.text}</Response>;
<div key={index} className="whitespace-pre-wrap">
{part.text}
</div>
);
} }
return null; return null;
})} })}

View File

@@ -5,6 +5,7 @@ import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Send } from "lucide-react"; import { Send } from "lucide-react";
import { useRef, useEffect, useState } from "react"; import { useRef, useEffect, useState } from "react";
import { Response } from "@/components/response";
export const Route = createFileRoute("/ai")({ export const Route = createFileRoute("/ai")({
component: RouteComponent, component: RouteComponent,
@@ -54,11 +55,7 @@ function RouteComponent() {
</p> </p>
{message.parts?.map((part, index) => { {message.parts?.map((part, index) => {
if (part.type === "text") { if (part.type === "text") {
return ( return <Response key={index}>{part.text}</Response>;
<div key={index} className="whitespace-pre-wrap">
{part.text}
</div>
);
} }
return null; return null;
})} })}

View File

@@ -1,5 +1,8 @@
@import "tailwindcss"; @import "tailwindcss";
@import "tw-animate-css"; @import "tw-animate-css";
{{#if (includes examples "ai")}}
@source "../node_modules/streamdown/dist/index.js";
{{/if}}
@custom-variant dark (&:where(.dark, .dark *)); @custom-variant dark (&:where(.dark, .dark *));