diff --git a/apps/next/src/app/(home)/page.tsx b/apps/next/src/app/(home)/page.tsx index d36bdcb1da232f0edbdcdb8866949a7dd1eb58c8..6bc39e61173ea89ed3489f754d865af90c964f59 100644 --- a/apps/next/src/app/(home)/page.tsx +++ b/apps/next/src/app/(home)/page.tsx @@ -10,10 +10,16 @@ import { MagicMove } from "@/components/magic-move"; import { NpmInstall } from "@/components/npm-install"; import { TextEffect } from "@/components/text-effect"; import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/ui/skeleton"; import { DOCUMENT_URL } from "@/lib/const"; import { SiStackblitz } from "@icons-pack/react-simple-icons"; +import { + CodeBlock as FumaCodeBlock, + Pre, +} from "fumadocs-ui/components/codeblock"; import { Blocks, Bot, Footprints, Terminal } from "lucide-react"; import Link from "next/link"; +import { Suspense } from "react"; export default function HomePage() { return ( @@ -55,17 +61,29 @@ export default function HomePage() { heading="Adding AI feature from simple to complex" description="LlamaIndex.TS is designed to be simple to start with and can be extended to build complex AI applications." > - <MagicMove - code={[ - `import { OpenAI } from "llamaindex"; + <Suspense + fallback={ + <FumaCodeBlock allowCopy={false}> + <Pre> + <div className="space-y-2"> + <Skeleton className="h-4 w-[250px]" /> + <Skeleton className="h-4 w-[200px]" /> + </div> + </Pre> + </FumaCodeBlock> + } + > + <MagicMove + code={[ + `import { OpenAI } from "llamaindex"; const llm = new OpenAI(); const response = await llm.complete({ prompt: "How are you?" });`, - `import { OpenAI } from "llamaindex"; + `import { OpenAI } from "llamaindex"; const llm = new OpenAI(); const response = await llm.chat({ messages: [{ content: "Tell me a joke.", role: "user" }], });`, - `import { OpenAI, ChatMemoryBuffer } from "llamaindex"; + `import { OpenAI, ChatMemoryBuffer } from "llamaindex"; const llm = new OpenAI({ model: 'gpt4o-turbo' }); const buffer = new ChatMemoryBuffer({ tokenLimit: 128_000, @@ -75,7 +93,7 @@ const response = await llm.chat({ messages: buffer.getMessages(), stream: true });`, - `import { OpenAIAgent, ChatMemoryBuffer } from "llamaindex"; + `import { OpenAIAgent, ChatMemoryBuffer } from "llamaindex"; const agent = new OpenAIAgent({ llm, tools: [...myTools] @@ -89,8 +107,9 @@ buffer.put({ content: \`\${data}\`, role: "user" }) const response = await agent.chat({ message: buffer.getMessages(), });`, - ]} - /> + ]} + /> + </Suspense> </Feature> <Feature icon={Bot} diff --git a/apps/next/src/components/magic-move.tsx b/apps/next/src/components/magic-move.tsx index 9d5f104d24eac3e5fba096066936f8521e33ee74..216d931f96bf48a527753676c3595a6c3af82091 100644 --- a/apps/next/src/components/magic-move.tsx +++ b/apps/next/src/components/magic-move.tsx @@ -4,12 +4,14 @@ import { cn } from "@/lib/utils"; import { CodeBlock, Pre } from "fumadocs-ui/components/codeblock"; import { RotateCcw } from "lucide-react"; import { useTheme } from "next-themes"; -import { useCallback, useEffect, useState } from "react"; -import { createHighlighter, type HighlighterCore } from "shiki"; +import { use, useCallback, useEffect, useState } from "react"; +import { getSingletonHighlighter } from "shiki"; import { ShikiMagicMove } from "shiki-magic-move/react"; +import { createOnigurumaEngine } from "shiki/engine/oniguruma"; -const highlighterPromise = createHighlighter({ - themes: ["github-light", "github-dark"], +const highlighterPromise = getSingletonHighlighter({ + engine: createOnigurumaEngine(() => import("shiki/wasm")), + themes: ["vesper", "github-light"], langs: ["js", "ts", "tsx"], }); @@ -20,17 +22,9 @@ export type MagicMoveProps = { export function MagicMove(props: MagicMoveProps) { const [move, setMove] = useState<number>(0); const currentCode = props.code[move]; - const [highlighter, setHighlighter] = useState<HighlighterCore>(); + const highlighter = use(highlighterPromise); const { resolvedTheme } = useTheme(); - useEffect(() => { - async function initializeHighlighter() { - setHighlighter(await highlighterPromise); - } - - initializeHighlighter().catch(console.error); - }, []); - const animate = useCallback(() => { setMove((move) => (move + 1) % props.code.length); }, [props.code]); @@ -50,7 +44,7 @@ export function MagicMove(props: MagicMoveProps) { <Pre> <ShikiMagicMove lang="ts" - theme={resolvedTheme === "dark" ? "github-dark" : "github-light"} + theme={resolvedTheme === "dark" ? "vesper" : "github-light"} highlighter={highlighter} code={currentCode} options={{ diff --git a/apps/next/src/components/ui/skeleton.tsx b/apps/next/src/components/ui/skeleton.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c26c4ea9f56a9cb11803999009c02158bd7be9fe --- /dev/null +++ b/apps/next/src/components/ui/skeleton.tsx @@ -0,0 +1,15 @@ +import { cn } from "@/lib/utils"; + +function Skeleton({ + className, + ...props +}: React.HTMLAttributes<HTMLDivElement>) { + return ( + <div + className={cn("animate-pulse rounded-md bg-primary/10", className)} + {...props} + /> + ); +} + +export { Skeleton };