mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-02 22:32:27 +08:00
feat: add WebSearchToolUI component and input UI, integrate Radix UI scroll area
This commit is contained in:
73
app/page.tsx
73
app/page.tsx
@@ -1,43 +1,38 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { AssistantRuntimeProvider } from "@assistant-ui/react";
|
|
||||||
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
||||||
import { DrawIoEmbed, DrawIoEmbedRef } from "react-drawio";
|
import { DrawIoEmbed, DrawIoEmbedRef } from "react-drawio";
|
||||||
|
|
||||||
import { Thread } from "@/components/assistant-ui/thread";
|
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { extractDiagramXML } from "./extract_xml"
|
import { extractDiagramXML } from "./extract_xml";
|
||||||
export default function Home() {
|
import ChatPanel from "@/components/chatPanel";
|
||||||
const runtime = useChatRuntime({
|
|
||||||
api: "/api/chat",
|
|
||||||
});
|
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
const drawioRef = useRef<DrawIoEmbedRef>(null);
|
const drawioRef = useRef<DrawIoEmbedRef>(null);
|
||||||
const [imgData, setImgData] = useState<string | null>(null);
|
const [chartXML, setChartXML] = useState<string>("");
|
||||||
const [diagram, setDiagram] = useState<string>("");
|
const [diagram, setDiagram] = useState<string>("");
|
||||||
// const handleExport = () => {};
|
// const handleExport = () => {};
|
||||||
const handleExport = () => {
|
const handleExport = () => {
|
||||||
|
// use this function to export the diagramxml from the drawio editor
|
||||||
if (drawioRef.current) {
|
if (drawioRef.current) {
|
||||||
drawioRef.current.exportDiagram({
|
drawioRef.current.exportDiagram({
|
||||||
format: "xmlsvg",
|
format: "xmlsvg",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const loadDiagram = (chart: string) => {
|
||||||
e.preventDefault();
|
// use this function to display the diagramxml in the drawio editor
|
||||||
if (drawioRef.current) {
|
if (drawioRef.current) {
|
||||||
drawioRef.current.load({
|
drawioRef.current.load({
|
||||||
xml: diagram,
|
xml: chart,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
console.log("imgData", imgData);
|
|
||||||
return (
|
return (
|
||||||
<AssistantRuntimeProvider runtime={runtime}>
|
<div className="flex h-screen bg-gray-100">
|
||||||
<div className="grid h-dvh grid-cols-[1fr_400px] gap-x-2 px-4 py-4">
|
<div className="w-2/3 p-1">
|
||||||
<DrawIoEmbed
|
<DrawIoEmbed
|
||||||
ref={drawioRef}
|
ref={drawioRef}
|
||||||
onExport={(data) => setImgData(data.data)}
|
onExport={(data) => setChartXML(extractDiagramXML(data.data))}
|
||||||
urlParameters={{
|
urlParameters={{
|
||||||
// ui: "kennedy",
|
// ui: "kennedy",
|
||||||
spin: true,
|
spin: true,
|
||||||
@@ -46,48 +41,10 @@ export default function Home() {
|
|||||||
noExitBtn: true,
|
noExitBtn: true,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* <Thread /> */}
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<form>
|
|
||||||
<div className="mb-4">
|
|
||||||
<label htmlFor="diagramXml" className="block text-sm font-medium mb-1">
|
|
||||||
Diagram XML
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="diagramXml"
|
|
||||||
className="w-full p-2 border border-gray-300 rounded-md min-h-[200px]"
|
|
||||||
placeholder="Paste your diagram XML here..."
|
|
||||||
value={diagram || ''}
|
|
||||||
onChange={(e) => setDiagram(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button onClick={handleSubmit}>submit</Button>
|
|
||||||
</form>
|
|
||||||
<div>
|
|
||||||
<div className="bg-amber-300 w-full h-[400px]">{imgData &&
|
|
||||||
<>
|
|
||||||
{/* <img src={imgData} /> */}
|
|
||||||
<div className="bg-blue-100 h-[400px] p-4 overflow-auto">
|
|
||||||
<h1 className="font-semibold mb-2">Extracted XML</h1>
|
|
||||||
{(() => {
|
|
||||||
try {
|
|
||||||
const extractedXml = extractDiagramXML(imgData);
|
|
||||||
return <pre className="whitespace-pre-wrap text-sm">{extractedXml}</pre>;
|
|
||||||
} catch (error) {
|
|
||||||
return (
|
|
||||||
<div className="text-red-600">
|
|
||||||
Error extracting XML: {error instanceof Error ? error.message : 'Unknown error'}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
<Button onClick={handleExport}>Export</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</AssistantRuntimeProvider>
|
<div className="w-1/3 p-1 border-gray-300 ">
|
||||||
|
<ChatPanel />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
87
components/chatPanel.tsx
Normal file
87
components/chatPanel.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import type React from "react"
|
||||||
|
|
||||||
|
import { useRef, useEffect } from "react"
|
||||||
|
import { useChat } from "@ai-sdk/react"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Input } from "@/components/ui/input"
|
||||||
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||||
|
import { Loader2, Send } from "lucide-react"
|
||||||
|
|
||||||
|
|
||||||
|
export default function ChatPanel() {
|
||||||
|
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
|
||||||
|
api: "/api/chat",
|
||||||
|
maxSteps: 5, // Allow multiple steps for complex diagram generation
|
||||||
|
})
|
||||||
|
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
// Scroll to bottom when messages change
|
||||||
|
useEffect(() => {
|
||||||
|
if (messagesEndRef.current) {
|
||||||
|
messagesEndRef.current.scrollIntoView({ behavior: "smooth" })
|
||||||
|
}
|
||||||
|
}, [messages])
|
||||||
|
|
||||||
|
|
||||||
|
const onFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (input.trim()) {
|
||||||
|
handleSubmit(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="h-full flex flex-col">
|
||||||
|
<CardHeader className="pb-2">
|
||||||
|
<CardTitle>Chat with Diagram Generator</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex-grow overflow-hidden p-4">
|
||||||
|
<ScrollArea className="h-full pr-4">
|
||||||
|
{messages.length === 0 ? (
|
||||||
|
<div className="text-center text-gray-500 mt-8">
|
||||||
|
<p>Start a conversation to generate a diagram.</p>
|
||||||
|
<p className="text-sm mt-2">Try: "Create a flowchart for user authentication"</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
messages.map((message) => (
|
||||||
|
<div key={message.id} className={`mb-4 ${message.role === "user" ? "text-right" : "text-left"}`}>
|
||||||
|
<div
|
||||||
|
className={`inline-block px-4 py-2 rounded-lg max-w-[85%] break-words ${message.role === "user" ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{message.content}
|
||||||
|
</div>
|
||||||
|
{message.toolInvocations?.map((tool, index) => (
|
||||||
|
<div key={index} className="mt-2 text-left">
|
||||||
|
<div className="text-xs text-gray-500">
|
||||||
|
{tool.state === "call" ? "Generating diagram..." : "Diagram generated"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</ScrollArea>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="pt-2">
|
||||||
|
<form onSubmit={onFormSubmit} className="w-full flex space-x-2">
|
||||||
|
<Input
|
||||||
|
value={input}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Describe the diagram you want to create..."
|
||||||
|
disabled={isLoading}
|
||||||
|
className="flex-grow"
|
||||||
|
/>
|
||||||
|
<Button type="submit" disabled={isLoading || !input.trim()}>
|
||||||
|
{isLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
92
components/ui/card.tsx
Normal file
92
components/ui/card.tsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card"
|
||||||
|
className={cn(
|
||||||
|
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-header"
|
||||||
|
className={cn(
|
||||||
|
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-[data-slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-title"
|
||||||
|
className={cn("leading-none font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-description"
|
||||||
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-action"
|
||||||
|
className={cn(
|
||||||
|
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-content"
|
||||||
|
className={cn("px-6", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-footer"
|
||||||
|
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardFooter,
|
||||||
|
CardTitle,
|
||||||
|
CardAction,
|
||||||
|
CardDescription,
|
||||||
|
CardContent,
|
||||||
|
}
|
||||||
21
components/ui/input.tsx
Normal file
21
components/ui/input.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
data-slot="input"
|
||||||
|
className={cn(
|
||||||
|
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||||
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Input }
|
||||||
58
components/ui/scroll-area.tsx
Normal file
58
components/ui/scroll-area.tsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function ScrollArea({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<ScrollAreaPrimitive.Root
|
||||||
|
data-slot="scroll-area"
|
||||||
|
className={cn("relative", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ScrollAreaPrimitive.Viewport
|
||||||
|
data-slot="scroll-area-viewport"
|
||||||
|
className="ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ScrollAreaPrimitive.Viewport>
|
||||||
|
<ScrollBar />
|
||||||
|
<ScrollAreaPrimitive.Corner />
|
||||||
|
</ScrollAreaPrimitive.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ScrollBar({
|
||||||
|
className,
|
||||||
|
orientation = "vertical",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
||||||
|
return (
|
||||||
|
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||||
|
data-slot="scroll-area-scrollbar"
|
||||||
|
orientation={orientation}
|
||||||
|
className={cn(
|
||||||
|
"flex touch-none p-px transition-colors select-none",
|
||||||
|
orientation === "vertical" &&
|
||||||
|
"h-full w-2.5 border-l border-l-transparent",
|
||||||
|
orientation === "horizontal" &&
|
||||||
|
"h-2.5 flex-col border-t border-t-transparent",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||||
|
data-slot="scroll-area-thumb"
|
||||||
|
className="bg-border relative flex-1 rounded-full"
|
||||||
|
/>
|
||||||
|
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ScrollArea, ScrollBar }
|
||||||
21
components/webSearchToolUI.tsx
Normal file
21
components/webSearchToolUI.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { makeAssistantToolUI } from "@assistant-ui/react";
|
||||||
|
|
||||||
|
type WebSearchArgs = {
|
||||||
|
query: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WebSearchResult = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WebSearchToolUI = makeAssistantToolUI<
|
||||||
|
WebSearchArgs,
|
||||||
|
WebSearchResult
|
||||||
|
>({
|
||||||
|
toolName: "web_search",
|
||||||
|
render: ({ args, status }) => {
|
||||||
|
return <p>web_search({args.query}) </p>;
|
||||||
|
},
|
||||||
|
});
|
||||||
53
package-lock.json
generated
53
package-lock.json
generated
@@ -13,6 +13,7 @@
|
|||||||
"@assistant-ui/react": "^0.8.6",
|
"@assistant-ui/react": "^0.8.6",
|
||||||
"@assistant-ui/react-ai-sdk": "^0.8.0",
|
"@assistant-ui/react-ai-sdk": "^0.8.0",
|
||||||
"@assistant-ui/react-markdown": "^0.8.0",
|
"@assistant-ui/react-markdown": "^0.8.0",
|
||||||
|
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@xmldom/xmldom": "^0.9.8",
|
"@xmldom/xmldom": "^0.9.8",
|
||||||
@@ -532,6 +533,12 @@
|
|||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/number": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/primitive": {
|
"node_modules/@radix-ui/primitive": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||||
@@ -591,6 +598,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-direction": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz",
|
||||||
@@ -816,6 +838,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-scroll-area": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/number": "1.1.0",
|
||||||
|
"@radix-ui/primitive": "1.1.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.1",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-direction": "1.1.0",
|
||||||
|
"@radix-ui/react-presence": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.0.2",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-slot": {
|
"node_modules/@radix-ui/react-slot": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"@assistant-ui/react": "^0.8.6",
|
"@assistant-ui/react": "^0.8.6",
|
||||||
"@assistant-ui/react-ai-sdk": "^0.8.0",
|
"@assistant-ui/react-ai-sdk": "^0.8.0",
|
||||||
"@assistant-ui/react-markdown": "^0.8.0",
|
"@assistant-ui/react-markdown": "^0.8.0",
|
||||||
|
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.1.8",
|
||||||
"@xmldom/xmldom": "^0.9.8",
|
"@xmldom/xmldom": "^0.9.8",
|
||||||
|
|||||||
Reference in New Issue
Block a user