├── .gitignore ├── .prettierignore ├── README.md ├── app.config.ts ├── app ├── client.tsx ├── components │ └── ui │ │ ├── button.tsx │ │ ├── card.tsx │ │ └── input.tsx ├── lib │ └── utils.ts ├── routeTree.gen.ts ├── router.tsx ├── routes │ ├── __root.tsx │ └── index.tsx ├── ssr.tsx └── styles │ └── app.css ├── components.json ├── curl-deepseek.sh ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── tailwind.config.js ├── tests ├── granite.mjs ├── hermes.mjs ├── nemotron.mjs └── qwen.mjs └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | yarn.lock 4 | 5 | .DS_Store 6 | .cache 7 | .env 8 | .vercel 9 | .output 10 | .vinxi 11 | 12 | /build/ 13 | /api/ 14 | /server/build 15 | /public/build 16 | .vinxi 17 | # Sentry Config File 18 | .env.sentry-build-plugin 19 | /test-results/ 20 | /playwright-report/ 21 | /blob-report/ 22 | /playwright/.cache/ 23 | 24 | count.txt 25 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/build 2 | **/public 3 | pnpm-lock.yaml 4 | routeTree.gen.ts -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to TanStack.com! 2 | 3 | This site is built with TanStack Router! 4 | 5 | - [TanStack Router Docs](https://tanstack.com/router) 6 | 7 | It's deployed automagically with Vercel! 8 | 9 | - [Vercel](https://vercel.com/) 10 | 11 | ## Development 12 | 13 | From your terminal: 14 | 15 | ```sh 16 | pnpm install 17 | pnpm dev 18 | ``` 19 | 20 | This starts your app in development mode, rebuilding assets on file changes. 21 | 22 | ## Editing and previewing the docs of TanStack projects locally 23 | 24 | The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app. 25 | In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system. 26 | 27 | Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally : 28 | 29 | 1. Create a new directory called `tanstack`. 30 | 31 | ```sh 32 | mkdir tanstack 33 | ``` 34 | 35 | 2. Enter the directory and clone this repo and the repo of the project there. 36 | 37 | ```sh 38 | cd tanstack 39 | git clone git@github.com:TanStack/tanstack.com.git 40 | git clone git@github.com:TanStack/form.git 41 | ``` 42 | 43 | > [!NOTE] 44 | > Your `tanstack` directory should look like this: 45 | > 46 | > ``` 47 | > tanstack/ 48 | > | 49 | > +-- form/ 50 | > | 51 | > +-- tanstack.com/ 52 | > ``` 53 | 54 | > [!WARNING] 55 | > Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found. 56 | 57 | 3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode: 58 | 59 | ```sh 60 | cd tanstack.com 61 | pnpm i 62 | # The app will run on https://localhost:3000 by default 63 | pnpm dev 64 | ``` 65 | 66 | 4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`. 67 | 68 | > [!NOTE] 69 | > The updated pages need to be manually reloaded in the browser. 70 | 71 | > [!WARNING] 72 | > You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page! 73 | -------------------------------------------------------------------------------- /app.config.ts: -------------------------------------------------------------------------------- 1 | // app.config.ts 2 | import { defineConfig } from '@tanstack/start/config' 3 | 4 | export default defineConfig({}) 5 | -------------------------------------------------------------------------------- /app/client.tsx: -------------------------------------------------------------------------------- 1 | // app/client.tsx 2 | /// 3 | import { hydrateRoot } from 'react-dom/client' 4 | import { StartClient } from '@tanstack/start' 5 | import { createRouter } from './router' 6 | 7 | const router = createRouter() 8 | 9 | hydrateRoot(document.getElementById('root')!, ) 10 | -------------------------------------------------------------------------------- /app/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Slot } from "@radix-ui/react-slot"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "../../lib/utils"; 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-10 w-10", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ); 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean; 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button"; 45 | return ( 46 | 51 | ); 52 | } 53 | ); 54 | Button.displayName = "Button"; 55 | 56 | export { Button, buttonVariants }; 57 | -------------------------------------------------------------------------------- /app/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "../../lib/utils"; 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )); 18 | Card.displayName = "Card"; 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )); 30 | CardHeader.displayName = "CardHeader"; 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )); 45 | CardTitle.displayName = "CardTitle"; 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )); 57 | CardDescription.displayName = "CardDescription"; 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )); 65 | CardContent.displayName = "CardContent"; 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )); 77 | CardFooter.displayName = "CardFooter"; 78 | 79 | export { 80 | Card, 81 | CardHeader, 82 | CardFooter, 83 | CardTitle, 84 | CardDescription, 85 | CardContent, 86 | }; 87 | -------------------------------------------------------------------------------- /app/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "../../lib/utils"; 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ); 21 | } 22 | ); 23 | Input.displayName = "Input"; 24 | 25 | export { Input }; 26 | -------------------------------------------------------------------------------- /app/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /app/routeTree.gen.ts: -------------------------------------------------------------------------------- 1 | /* prettier-ignore-start */ 2 | 3 | /* eslint-disable */ 4 | 5 | // @ts-nocheck 6 | 7 | // noinspection JSUnusedGlobalSymbols 8 | 9 | // This file is auto-generated by TanStack Router 10 | 11 | // Import Routes 12 | 13 | import { Route as rootRoute } from './routes/__root' 14 | import { Route as IndexImport } from './routes/index' 15 | 16 | // Create/Update Routes 17 | 18 | const IndexRoute = IndexImport.update({ 19 | id: '/', 20 | path: '/', 21 | getParentRoute: () => rootRoute, 22 | } as any) 23 | 24 | // Populate the FileRoutesByPath interface 25 | 26 | declare module '@tanstack/react-router' { 27 | interface FileRoutesByPath { 28 | '/': { 29 | id: '/' 30 | path: '/' 31 | fullPath: '/' 32 | preLoaderRoute: typeof IndexImport 33 | parentRoute: typeof rootRoute 34 | } 35 | } 36 | } 37 | 38 | // Create and export the route tree 39 | 40 | export interface FileRoutesByFullPath { 41 | '/': typeof IndexRoute 42 | } 43 | 44 | export interface FileRoutesByTo { 45 | '/': typeof IndexRoute 46 | } 47 | 48 | export interface FileRoutesById { 49 | __root__: typeof rootRoute 50 | '/': typeof IndexRoute 51 | } 52 | 53 | export interface FileRouteTypes { 54 | fileRoutesByFullPath: FileRoutesByFullPath 55 | fullPaths: '/' 56 | fileRoutesByTo: FileRoutesByTo 57 | to: '/' 58 | id: '__root__' | '/' 59 | fileRoutesById: FileRoutesById 60 | } 61 | 62 | export interface RootRouteChildren { 63 | IndexRoute: typeof IndexRoute 64 | } 65 | 66 | const rootRouteChildren: RootRouteChildren = { 67 | IndexRoute: IndexRoute, 68 | } 69 | 70 | export const routeTree = rootRoute 71 | ._addFileChildren(rootRouteChildren) 72 | ._addFileTypes() 73 | 74 | /* prettier-ignore-end */ 75 | 76 | /* ROUTE_MANIFEST_START 77 | { 78 | "routes": { 79 | "__root__": { 80 | "filePath": "__root.tsx", 81 | "children": [ 82 | "/" 83 | ] 84 | }, 85 | "/": { 86 | "filePath": "index.tsx" 87 | } 88 | } 89 | } 90 | ROUTE_MANIFEST_END */ 91 | -------------------------------------------------------------------------------- /app/router.tsx: -------------------------------------------------------------------------------- 1 | // app/router.tsx 2 | import { createRouter as createTanStackRouter } from '@tanstack/react-router' 3 | import { routeTree } from './routeTree.gen' 4 | 5 | export function createRouter() { 6 | const router = createTanStackRouter({ 7 | routeTree, 8 | }) 9 | 10 | return router 11 | } 12 | 13 | declare module '@tanstack/react-router' { 14 | interface Register { 15 | router: ReturnType 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/routes/__root.tsx: -------------------------------------------------------------------------------- 1 | // app/routes/__root.tsx 2 | import { createRootRoute } from "@tanstack/react-router"; 3 | import { Outlet, ScrollRestoration } from "@tanstack/react-router"; 4 | import { Body, Head, Html, Meta, Scripts } from "@tanstack/start"; 5 | import * as React from "react"; 6 | import appCss from "../styles/app.css?url" 7 | 8 | 9 | export const Route = createRootRoute({ 10 | meta: () => [ 11 | { 12 | charSet: "utf-8", 13 | }, 14 | { 15 | name: "viewport", 16 | content: "width=device-width, initial-scale=1", 17 | }, 18 | { 19 | title: "Start + Deepseek + Ollama", 20 | }, 21 | ], 22 | links: () => [ 23 | { rel: 'stylesheet', href: appCss }, 24 | ], 25 | component: RootComponent, 26 | }); 27 | 28 | function RootComponent() { 29 | return ( 30 | 31 | 32 | 33 | ); 34 | } 35 | 36 | function RootDocument({ children }: { children: React.ReactNode }) { 37 | return ( 38 | 39 | 40 | 41 | 42 | 43 | {children} 44 | 45 | 46 | 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /app/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute } from "@tanstack/react-router"; 2 | import { createServerFn } from "@tanstack/start"; 3 | import { Bot, Loader2, MessageSquare, Send, User2 } from "lucide-react"; 4 | import { useMemo, useState } from "react"; 5 | import Markdown from "react-markdown"; 6 | 7 | import { Button } from "../components/ui/button"; 8 | import { Input } from "../components/ui/input"; 9 | 10 | type Message = { 11 | role: "user" | "assistant" | "tool" | "system"; 12 | content: string; 13 | }; 14 | 15 | type MessageWithThinking = Message & { 16 | finishedThinking?: boolean; 17 | think?: string; 18 | }; 19 | 20 | function useMessagesWithThinking(messages: Message[]) { 21 | return useMemo( 22 | () => 23 | messages.map((m: Message): MessageWithThinking => { 24 | if (m.role === "assistant") { 25 | if (m.content.includes("")) { 26 | return { 27 | ...m, 28 | finishedThinking: true, 29 | think: m.content 30 | .split("")[0] 31 | .replace("", "") 32 | .replace("", ""), 33 | content: m.content.split("")[1], 34 | }; 35 | } else { 36 | return { 37 | ...m, 38 | finishedThinking: false, 39 | think: m.content.replace("", ""), 40 | content: "", 41 | }; 42 | } 43 | } 44 | return m; 45 | }), 46 | [messages] 47 | ); 48 | } 49 | 50 | function streamAsyncIterator(reader: ReadableStreamDefaultReader) { 51 | const decoder = new TextDecoder("utf-8"); 52 | return { 53 | async *[Symbol.asyncIterator]() { 54 | try { 55 | while (true) { 56 | const { done, value } = await reader.read(); 57 | if (done) return; 58 | yield decoder.decode(value); 59 | } 60 | } finally { 61 | reader.releaseLock(); 62 | } 63 | }, 64 | }; 65 | } 66 | 67 | export const Route = createFileRoute("/")({ 68 | component: AIChat, 69 | }); 70 | 71 | const chat = createServerFn( 72 | "POST", 73 | async ({ messages }: { messages: Message[] }) => { 74 | return fetch("http://127.0.0.1:11434/api/chat", { 75 | method: "POST", 76 | headers: { 77 | "Content-Type": "application/json", 78 | }, 79 | body: JSON.stringify({ 80 | model: "deepseek-r1:32b", 81 | streaming: true, 82 | options: { 83 | temperature: 0.1, 84 | repeat_penalty: 1.2, 85 | numa: true, // testing for ARM 86 | }, 87 | messages: [...messages], 88 | }), 89 | }); 90 | } 91 | ); 92 | 93 | function AIChat() { 94 | const [messages, setMessages] = useState([]); 95 | const [input, setInput] = useState(""); 96 | const [premise, setPremise] = useState("You are a software developer with a focus on React/TypeScript.\rKeep your answer simple and straight forward."); 97 | const [loading, setLoading] = useState(false); 98 | 99 | const handleSubmit = async (e: React.FormEvent) => { 100 | e.preventDefault(); 101 | setInput(""); 102 | setLoading(true); 103 | 104 | const messagesWithInput: Message[] = [ 105 | ...messages, 106 | { role: "system", content: premise }, 107 | { role: "user", content: input }, 108 | ]; 109 | setMessages(messagesWithInput); 110 | 111 | const stream = await chat({ messages: messagesWithInput }); 112 | if (stream.body) { 113 | let assistantResponse = ""; 114 | const reader = stream.body.getReader(); 115 | for await (const value of streamAsyncIterator(reader)) { 116 | const { 117 | message: { content }, 118 | } = JSON.parse(value); 119 | assistantResponse += content; 120 | setMessages([ 121 | ...messagesWithInput, 122 | { 123 | role: "assistant", 124 | content: assistantResponse, 125 | }, 126 | ]); 127 | } 128 | } 129 | setLoading(false); 130 | }; 131 | 132 | const messagesWithThinkingSplit = useMessagesWithThinking(messages); 133 | 134 | return ( 135 |
136 |
137 |