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/svelte": "^3.0.9",
"@ai-sdk/react": "^2.0.9",
streamdown: "^1.1.6",
"@orpc/server": "^1.8.4",
"@orpc/client": "^1.8.4",

View File

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

View File

@@ -740,6 +740,19 @@ export async function setupExamplesTemplate(
if (hasReactWeb) {
const exampleWebSrc = path.join(exampleBaseDir, "web/react");
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) =>
[
"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 { DefaultChatTransport } from "ai";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
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() {
const [input, setInput] = useState("");
@@ -51,11 +52,7 @@ export default function AIPage() {
</p>
{message.parts?.map((part, index) => {
if (part.type === "text") {
return (
<div key={index} className="whitespace-pre-wrap">
{part.text}
</div>
);
return <Response key={index}>{part.text}</Response>;
}
return null;
})}

View File

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

View File

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

View File

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

View File

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