Skip to content

Commit e587993

Browse files
committed
chore: sync up with main and update ai-elements components
1 parent f54959f commit e587993

32 files changed

+1860
-4343
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ node_modules/
1313
dist/
1414
build/
1515
lib/
16-
!src/**/lib
16+
!/**/src/**/lib
1717

1818
# Cloudflare Workers
1919
.wrangler/
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { dirname } from "path";
22
import { fileURLToPath } from "url";
33
import { FlatCompat } from "@eslint/eslintrc";
4+
import { defineConfig, globalIgnores } from "eslint/config";
45

56
const __filename = fileURLToPath(import.meta.url);
67
const __dirname = dirname(__filename);
@@ -9,8 +10,9 @@ const compat = new FlatCompat({
910
baseDirectory: __dirname,
1011
});
1112

12-
const eslintConfig = [
13+
const eslintConfig = defineConfig([
1314
...compat.extends("next/core-web-vitals", "next/typescript"),
14-
];
15+
globalIgnores(["src/**/ai-elements/**"]),
16+
]);
1517

1618
export default eslintConfig;

packages/playground/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"@radix-ui/react-hover-card": "^1.1.15",
5353
"@radix-ui/react-icons": "^1.3.2",
5454
"@radix-ui/react-label": "^2.1.7",
55-
"@radix-ui/react-progress": "^1.1.7",
55+
"@radix-ui/react-progress": "^1.1.8",
5656
"@radix-ui/react-scroll-area": "^1.2.10",
5757
"@radix-ui/react-select": "^2.2.6",
5858
"@radix-ui/react-separator": "^1.1.8",
@@ -61,8 +61,8 @@
6161
"@radix-ui/react-tooltip": "^1.2.8",
6262
"@radix-ui/react-use-controllable-state": "^1.2.2",
6363
"@types/react-syntax-highlighter": "^15.5.13",
64-
"@xyflow/react": "^12.9.2",
65-
"ai": "^5.0.27",
64+
"@xyflow/react": "^12.9.3",
65+
"ai": "^5.0.97",
6666
"class-variance-authority": "^0.7.1",
6767
"clsx": "^2.1.1",
6868
"cmdk": "^1.1.1",
@@ -76,10 +76,10 @@
7676
"react-dom": "19.1.1",
7777
"react-hook-form": "^7.62.0",
7878
"react-syntax-highlighter": "^15.6.6",
79-
"shiki": "^3.12.2",
80-
"streamdown": "^1.2.0",
79+
"shiki": "^3.15.0",
80+
"streamdown": "^1.5.1",
8181
"tailwind-merge": "^3.3.1",
82-
"tokenlens": "^1.1.2",
82+
"tokenlens": "^1.3.1",
8383
"use-stick-to-bottom": "^1.1.1",
8484
"zod": "^4.1.5"
8585
},

packages/playground/src/components/add-agent-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export function AddAgentModal({
8282
if (!result.isOnline) {
8383
setError(`Connection failed: ${result.error}`);
8484
}
85-
} catch (err) {
85+
} catch {
8686
setError("Failed to test connection");
8787
setConnectionStatus({ tested: true, online: false });
8888
} finally {

packages/playground/src/components/ai-elements/chain-of-thought.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import {
1414
DotIcon,
1515
type LucideIcon,
1616
} from "lucide-react";
17-
import type { ComponentProps } from "react";
18-
import { createContext, memo, useContext } from "react";
17+
import type { ComponentProps, ReactNode } from "react";
18+
import { createContext, memo, useContext, useMemo } from "react";
1919

2020
type ChainOfThoughtContextValue = {
2121
isOpen: boolean;
@@ -57,8 +57,13 @@ export const ChainOfThought = memo(
5757
onChange: onOpenChange,
5858
});
5959

60+
const chainOfThoughtContext = useMemo(
61+
() => ({ isOpen, setIsOpen }),
62+
[isOpen, setIsOpen]
63+
);
64+
6065
return (
61-
<ChainOfThoughtContext.Provider value={{ isOpen, setIsOpen }}>
66+
<ChainOfThoughtContext.Provider value={chainOfThoughtContext}>
6267
<div
6368
className={cn("not-prose max-w-prose space-y-4", className)}
6469
{...props}
@@ -105,8 +110,8 @@ export const ChainOfThoughtHeader = memo(
105110

106111
export type ChainOfThoughtStepProps = ComponentProps<"div"> & {
107112
icon?: LucideIcon;
108-
label: string;
109-
description?: string;
113+
label: ReactNode;
114+
description?: ReactNode;
110115
status?: "complete" | "active" | "pending";
111116
};
112117

packages/playground/src/components/ai-elements/code-block.tsx

Lines changed: 105 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@
33
import { Button } from "@/components/ui/button";
44
import { cn } from "@/lib/utils";
55
import { CheckIcon, CopyIcon } from "lucide-react";
6-
import type { ComponentProps, HTMLAttributes, ReactNode } from "react";
7-
import { createContext, useContext, useState } from "react";
8-
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
96
import {
10-
oneDark,
11-
oneLight,
12-
} from "react-syntax-highlighter/dist/esm/styles/prism";
7+
type ComponentProps,
8+
createContext,
9+
type HTMLAttributes,
10+
useContext,
11+
useEffect,
12+
useRef,
13+
useState,
14+
} from "react";
15+
import { type BundledLanguage, codeToHtml, type ShikiTransformer } from "shiki";
16+
17+
type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
18+
code: string;
19+
language: BundledLanguage;
20+
showLineNumbers?: boolean;
21+
};
1322

1423
type CodeBlockContextType = {
1524
code: string;
@@ -19,85 +28,106 @@ const CodeBlockContext = createContext<CodeBlockContextType>({
1928
code: "",
2029
});
2130

22-
export type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
23-
code: string;
24-
language: string;
25-
showLineNumbers?: boolean;
26-
children?: ReactNode;
31+
const lineNumberTransformer: ShikiTransformer = {
32+
name: "line-numbers",
33+
line(node, line) {
34+
node.children.unshift({
35+
type: "element",
36+
tagName: "span",
37+
properties: {
38+
className: [
39+
"inline-block",
40+
"min-w-10",
41+
"mr-4",
42+
"text-right",
43+
"select-none",
44+
"text-muted-foreground",
45+
],
46+
},
47+
children: [{ type: "text", value: String(line) }],
48+
});
49+
},
2750
};
2851

52+
export async function highlightCode(
53+
code: string,
54+
language: BundledLanguage,
55+
showLineNumbers = false
56+
) {
57+
const transformers: ShikiTransformer[] = showLineNumbers
58+
? [lineNumberTransformer]
59+
: [];
60+
61+
return await Promise.all([
62+
codeToHtml(code, {
63+
lang: language,
64+
theme: "one-light",
65+
transformers,
66+
}),
67+
codeToHtml(code, {
68+
lang: language,
69+
theme: "one-dark-pro",
70+
transformers,
71+
}),
72+
]);
73+
}
74+
2975
export const CodeBlock = ({
3076
code,
3177
language,
3278
showLineNumbers = false,
3379
className,
3480
children,
3581
...props
36-
}: CodeBlockProps) => (
37-
<CodeBlockContext.Provider value={{ code }}>
38-
<div
39-
className={cn(
40-
"relative w-full overflow-hidden rounded-md border bg-background text-foreground",
41-
className,
42-
)}
43-
{...props}
44-
>
45-
<div className="relative">
46-
<SyntaxHighlighter
47-
className="overflow-hidden dark:hidden"
48-
codeTagProps={{
49-
className: "font-mono text-sm",
50-
}}
51-
customStyle={{
52-
margin: 0,
53-
padding: "1rem",
54-
fontSize: "0.875rem",
55-
background: "hsl(var(--background))",
56-
color: "hsl(var(--foreground))",
57-
}}
58-
language={language}
59-
lineNumberStyle={{
60-
color: "hsl(var(--muted-foreground))",
61-
paddingRight: "1rem",
62-
minWidth: "2.5rem",
63-
}}
64-
showLineNumbers={showLineNumbers}
65-
style={oneLight}
66-
>
67-
{code}
68-
</SyntaxHighlighter>
69-
<SyntaxHighlighter
70-
className="hidden overflow-hidden dark:block"
71-
codeTagProps={{
72-
className: "font-mono text-sm",
73-
}}
74-
customStyle={{
75-
margin: 0,
76-
padding: "1rem",
77-
fontSize: "0.875rem",
78-
background: "hsl(var(--background))",
79-
color: "hsl(var(--foreground))",
80-
}}
81-
language={language}
82-
lineNumberStyle={{
83-
color: "hsl(var(--muted-foreground))",
84-
paddingRight: "1rem",
85-
minWidth: "2.5rem",
86-
}}
87-
showLineNumbers={showLineNumbers}
88-
style={oneDark}
89-
>
90-
{code}
91-
</SyntaxHighlighter>
92-
{children && (
93-
<div className="absolute top-2 right-2 flex items-center gap-2">
94-
{children}
95-
</div>
82+
}: CodeBlockProps) => {
83+
const [html, setHtml] = useState<string>("");
84+
const [darkHtml, setDarkHtml] = useState<string>("");
85+
const mounted = useRef(false);
86+
87+
useEffect(() => {
88+
highlightCode(code, language, showLineNumbers).then(([light, dark]) => {
89+
if (!mounted.current) {
90+
setHtml(light);
91+
setDarkHtml(dark);
92+
mounted.current = true;
93+
}
94+
});
95+
96+
return () => {
97+
mounted.current = false;
98+
};
99+
}, [code, language, showLineNumbers]);
100+
101+
return (
102+
<CodeBlockContext.Provider value={{ code }}>
103+
<div
104+
className={cn(
105+
"group relative w-full overflow-hidden rounded-md border bg-background text-foreground",
106+
className
96107
)}
108+
{...props}
109+
>
110+
<div className="relative">
111+
<div
112+
className="overflow-hidden dark:hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
113+
// biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
114+
dangerouslySetInnerHTML={{ __html: html }}
115+
/>
116+
<div
117+
className="hidden overflow-hidden dark:block [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
118+
// biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
119+
dangerouslySetInnerHTML={{ __html: darkHtml }}
120+
/>
121+
{children && (
122+
<div className="absolute top-2 right-2 flex items-center gap-2">
123+
{children}
124+
</div>
125+
)}
126+
</div>
97127
</div>
98-
</div>
99-
</CodeBlockContext.Provider>
100-
);
128+
</CodeBlockContext.Provider>
129+
);
130+
};
101131

102132
export type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {
103133
onCopy?: () => void;
@@ -117,7 +147,7 @@ export const CodeBlockCopyButton = ({
117147
const { code } = useContext(CodeBlockContext);
118148

119149
const copyToClipboard = async () => {
120-
if (typeof window === "undefined" || !navigator.clipboard.writeText) {
150+
if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
121151
onError?.(new Error("Clipboard API not available"));
122152
return;
123153
}

packages/playground/src/components/ai-elements/confirmation.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ export type ConfirmationRequestProps = {
9696
export const ConfirmationRequest = ({ children }: ConfirmationRequestProps) => {
9797
const { state } = useConfirmation();
9898

99+
// Only show when approval is requested
100+
//@ts-ignore
101+
if (state !== "approval-requested") {
102+
return null;
103+
}
104+
99105
return children;
100106
};
101107

@@ -109,7 +115,14 @@ export const ConfirmationAccepted = ({
109115
const { approval, state } = useConfirmation();
110116

111117
// Only show when approved and in response states
112-
if (!approval?.approved || state !== "output-available") {
118+
if (
119+
!approval?.approved ||
120+
//@ts-ignore
121+
(state !== "approval-responded" &&
122+
//@ts-ignore
123+
state !== "output-denied" &&
124+
state !== "output-available")
125+
) {
113126
return null;
114127
}
115128

@@ -126,7 +139,14 @@ export const ConfirmationRejected = ({
126139
const { approval, state } = useConfirmation();
127140

128141
// Only show when rejected and in response states
129-
if (approval?.approved !== false || state !== "output-available") {
142+
if (
143+
approval?.approved !== false ||
144+
//@ts-ignore
145+
(state !== "approval-responded" &&
146+
//@ts-ignore
147+
state !== "output-denied" &&
148+
state !== "output-available")
149+
) {
130150
return null;
131151
}
132152

@@ -141,6 +161,12 @@ export const ConfirmationActions = ({
141161
}: ConfirmationActionsProps) => {
142162
const { state } = useConfirmation();
143163

164+
// Only show when approval is requested
165+
//@ts-ignore
166+
if (state !== "approval-requested") {
167+
return null;
168+
}
169+
144170
return (
145171
<div
146172
className={cn("flex items-center justify-end gap-2 self-end", className)}

0 commit comments

Comments
 (0)