create-tsrouter-app 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -9
- package/dist/add-ons.js +69 -0
- package/dist/cli.js +78 -0
- package/dist/constants.js +4 -0
- package/dist/create-app.js +371 -0
- package/dist/index.js +2 -347
- package/dist/mcp.js +169 -0
- package/dist/options.js +260 -0
- package/dist/{utils/getPackageManager.js → package-manager.js} +1 -0
- package/dist/types.js +1 -0
- package/package.json +6 -3
- package/src/add-ons.ts +156 -0
- package/src/cli.ts +114 -0
- package/src/constants.ts +7 -0
- package/src/create-app.ts +582 -0
- package/src/index.ts +2 -507
- package/src/mcp.ts +205 -0
- package/src/options.ts +308 -0
- package/src/{utils/getPackageManager.ts → package-manager.ts} +1 -0
- package/src/types.ts +30 -0
- package/templates/react/add-on/clerk/README.md +3 -0
- package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
- package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
- package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
- package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
- package/templates/react/add-on/clerk/info.json +13 -0
- package/templates/react/add-on/clerk/package.json +5 -0
- package/templates/react/add-on/convex/README.md +4 -0
- package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
- package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
- package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
- package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
- package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
- package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
- package/templates/react/add-on/convex/info.json +13 -0
- package/templates/react/add-on/convex/package.json +6 -0
- package/templates/react/add-on/form/assets/src/routes/demo.form.tsx.ejs +62 -0
- package/templates/react/add-on/form/info.json +13 -0
- package/templates/react/add-on/form/package.json +5 -0
- package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
- package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
- package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
- package/templates/react/add-on/module-federation/info.json +7 -0
- package/templates/react/add-on/module-federation/package.json +5 -0
- package/templates/react/add-on/netlify/README.md +11 -0
- package/templates/react/add-on/netlify/info.json +7 -0
- package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
- package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
- package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
- package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
- package/templates/react/add-on/sentry/info.json +14 -0
- package/templates/react/add-on/sentry/package.json +7 -0
- package/templates/react/add-on/shadcn/README.md +7 -0
- package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
- package/templates/react/add-on/shadcn/info.json +11 -0
- package/templates/react/add-on/start/assets/app.config.ts +16 -0
- package/templates/react/add-on/start/assets/postcss.config.ts +5 -0
- package/templates/react/add-on/start/assets/src/api.ts +6 -0
- package/templates/react/add-on/start/assets/src/client.tsx +10 -0
- package/templates/react/add-on/start/assets/src/router.tsx.ejs +51 -0
- package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
- package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
- package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
- package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
- package/templates/react/add-on/start/info.json +19 -0
- package/templates/react/add-on/start/package.json +14 -0
- package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
- package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
- package/templates/react/add-on/store/info.json +13 -0
- package/templates/react/add-on/store/package.json +6 -0
- package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
- package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +9 -0
- package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +38 -0
- package/templates/react/add-on/tanstack-query/info.json +13 -0
- package/templates/react/add-on/tanstack-query/package.json +6 -0
- package/templates/{base → react/base}/README.md.ejs +9 -0
- package/templates/react/base/_dot_vscode/settings.json +11 -0
- package/templates/react/base/src/components/Header.tsx.ejs +25 -0
- package/templates/{base/tsconfig.json → react/base/tsconfig.json.ejs} +5 -1
- package/templates/react/base/vite.config.js.ejs +24 -0
- package/templates/{code-router → react/code-router}/src/main.tsx.ejs +17 -1
- package/templates/react/example/tanchat/README.md +37 -0
- package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
- package/templates/react/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +148 -0
- package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
- package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx.ejs +375 -0
- package/templates/react/example/tanchat/assets/src/store/demo.hooks.ts +21 -0
- package/templates/react/example/tanchat/assets/src/store/demo.store.ts +133 -0
- package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +108 -0
- package/templates/react/example/tanchat/info.json +15 -0
- package/templates/react/example/tanchat/package.json +10 -0
- package/templates/react/file-router/src/routes/__root.tsx.ejs +71 -0
- package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +148 -0
- package/templates/solid/add-on/form/info.json +13 -0
- package/templates/solid/add-on/form/package.json +5 -0
- package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
- package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
- package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
- package/templates/solid/add-on/module-federation/info.json +7 -0
- package/templates/solid/add-on/module-federation/package.json +5 -0
- package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
- package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
- package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
- package/templates/solid/add-on/sentry/info.json +13 -0
- package/templates/solid/add-on/sentry/package.json +5 -0
- package/templates/solid/add-on/solid-ui/README.md +9 -0
- package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
- package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
- package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
- package/templates/solid/add-on/solid-ui/info.json +11 -0
- package/templates/solid/add-on/solid-ui/package.json +9 -0
- package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
- package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
- package/templates/solid/add-on/store/info.json +13 -0
- package/templates/solid/add-on/store/package.json +6 -0
- package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
- package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
- package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
- package/templates/solid/add-on/tanstack-query/info.json +13 -0
- package/templates/solid/add-on/tanstack-query/package.json +6 -0
- package/templates/solid/base/README.md.ejs +200 -0
- package/templates/solid/base/_dot_cursorrules.append +35 -0
- package/templates/solid/base/_dot_gitignore +5 -0
- package/templates/solid/base/_dot_vscode/settings.json +11 -0
- package/templates/solid/base/index.html.ejs +20 -0
- package/templates/solid/base/package.json +22 -0
- package/templates/solid/base/package.ts.json +5 -0
- package/templates/solid/base/package.tw.json +6 -0
- package/templates/solid/base/public/favicon.ico +0 -0
- package/templates/solid/base/public/logo192.png +0 -0
- package/templates/solid/base/public/logo512.png +0 -0
- package/templates/solid/base/public/manifest.json +25 -0
- package/templates/solid/base/public/robots.txt +3 -0
- package/templates/solid/base/src/App.css +0 -0
- package/templates/solid/base/src/App.tsx.ejs +47 -0
- package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
- package/templates/solid/base/src/logo.svg +120 -0
- package/templates/solid/base/src/styles.css.ejs +15 -0
- package/templates/solid/base/tsconfig.json.ejs +30 -0
- package/templates/solid/base/vite.config.js.ejs +22 -0
- package/templates/solid/code-router/src/main.tsx.ejs +69 -0
- package/templates/solid/file-router/package.fr.json +5 -0
- package/templates/solid/file-router/src/main.tsx.ejs +44 -0
- package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
- package/templates/solid/file-router/src/routes/index.tsx +43 -0
- package/templates/base/vite.config.js.ejs +0 -15
- package/templates/file-router/src/routes/__root.tsx +0 -11
- /package/templates/{base/gitignore → react/base/_dot_gitignore} +0 -0
- /package/templates/{base → react/base}/index.html.ejs +0 -0
- /package/templates/{base → react/base}/package.json +0 -0
- /package/templates/{base → react/base}/package.ts.json +0 -0
- /package/templates/{base → react/base}/package.tw.json +0 -0
- /package/templates/{base → react/base}/public/favicon.ico +0 -0
- /package/templates/{base → react/base}/public/logo192.png +0 -0
- /package/templates/{base → react/base}/public/logo512.png +0 -0
- /package/templates/{base → react/base}/public/manifest.json +0 -0
- /package/templates/{base → react/base}/public/robots.txt +0 -0
- /package/templates/{base → react/base}/src/App.css +0 -0
- /package/templates/{base → react/base}/src/App.test.tsx.ejs +0 -0
- /package/templates/{base → react/base}/src/App.tsx.ejs +0 -0
- /package/templates/{base → react/base}/src/logo.svg +0 -0
- /package/templates/{base → react/base}/src/reportWebVitals.ts.ejs +0 -0
- /package/templates/{base → react/base}/src/styles.css.ejs +0 -0
- /package/templates/{file-router → react/file-router}/package.fr.json +0 -0
- /package/templates/{file-router → react/file-router}/src/main.tsx.ejs +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<% if (addOnEnabled['react-query']) { %>
|
|
2
|
+
import { useQuery } from '@tanstack/react-query'
|
|
3
|
+
<% } else { %>
|
|
4
|
+
import { useEffect, useState } from 'react'
|
|
5
|
+
<% } %>
|
|
6
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
7
|
+
|
|
8
|
+
function getNames() {
|
|
9
|
+
return fetch('/api/demo-names').then((res) => res.json())
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Route = createFileRoute('/demo/start/api-request')({
|
|
13
|
+
component: Home,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
function Home() {
|
|
17
|
+
<% if (addOnEnabled['react-query']) { %>
|
|
18
|
+
const { data: names = [] } = useQuery({
|
|
19
|
+
queryKey: ['names'],
|
|
20
|
+
queryFn: getNames,
|
|
21
|
+
})
|
|
22
|
+
<% } else { %>
|
|
23
|
+
const [names, setNames] = useState<Array<string>>([])
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
getNames().then(setNames)
|
|
26
|
+
}, [])
|
|
27
|
+
<% } %>
|
|
28
|
+
return (
|
|
29
|
+
<div className="p-4">
|
|
30
|
+
<div>{names.join(', ')}</div>
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as fs from 'fs'
|
|
2
|
+
import { createFileRoute, useRouter } from '@tanstack/react-router'
|
|
3
|
+
import { createServerFn } from '@tanstack/start'
|
|
4
|
+
|
|
5
|
+
const filePath = 'count.txt'
|
|
6
|
+
|
|
7
|
+
async function readCount() {
|
|
8
|
+
return parseInt(
|
|
9
|
+
await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const getCount = createServerFn({
|
|
14
|
+
method: 'GET',
|
|
15
|
+
}).handler(() => {
|
|
16
|
+
return readCount()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const updateCount = createServerFn({ method: 'POST' })
|
|
20
|
+
.validator((d: number) => d)
|
|
21
|
+
.handler(async ({ data }) => {
|
|
22
|
+
const count = await readCount()
|
|
23
|
+
await fs.promises.writeFile(filePath, `${count + data}`)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export const Route = createFileRoute('/demo/start/server-funcs')({
|
|
27
|
+
component: Home,
|
|
28
|
+
loader: async () => await getCount(),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
function Home() {
|
|
32
|
+
const router = useRouter()
|
|
33
|
+
const state = Route.useLoaderData()
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="p-4">
|
|
37
|
+
<button
|
|
38
|
+
onClick={() => {
|
|
39
|
+
updateCount({ data: 1 }).then(() => {
|
|
40
|
+
router.invalidate()
|
|
41
|
+
})
|
|
42
|
+
}}
|
|
43
|
+
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
|
44
|
+
>
|
|
45
|
+
Add 1 to {state}?
|
|
46
|
+
</button>
|
|
47
|
+
</div>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createStartHandler,
|
|
3
|
+
defaultStreamHandler,
|
|
4
|
+
} from '@tanstack/start/server'
|
|
5
|
+
import { getRouterManifest } from '@tanstack/start/router-manifest'
|
|
6
|
+
|
|
7
|
+
import { createRouter } from './router'
|
|
8
|
+
|
|
9
|
+
export default createStartHandler({
|
|
10
|
+
createRouter,
|
|
11
|
+
getRouterManifest,
|
|
12
|
+
})(defaultStreamHandler)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Start",
|
|
3
|
+
"phase": "setup",
|
|
4
|
+
"description": "Add TanStack Start for SSR, API endpoints, and more.",
|
|
5
|
+
"link": "https://tanstack.com/start/latest",
|
|
6
|
+
"templates": ["file-router"],
|
|
7
|
+
"warning": "TanStack Start is not yet at 1.0 and may change significantly or not be compatible with other add-ons.",
|
|
8
|
+
"routes": [
|
|
9
|
+
{
|
|
10
|
+
"url": "/demo/start/server-funcs",
|
|
11
|
+
"name": "Start - Server Functions"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"url": "/demo/start/api-request",
|
|
15
|
+
"name": "Start - API Request"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"shadcnComponents": ["button", "input"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scripts": {
|
|
3
|
+
"dev": "vinxi dev",
|
|
4
|
+
"build": "vinxi build",
|
|
5
|
+
"start": "vinxi start"
|
|
6
|
+
},
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@tailwindcss/postcss": "^4.0.7",
|
|
9
|
+
"@tanstack/start": "^1.106.0",
|
|
10
|
+
"postcss": "^8.5.2",
|
|
11
|
+
"vinxi": "^0.5.3",
|
|
12
|
+
"vite-tsconfig-paths": "^5.1.4"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Derived, Store } from '@tanstack/store'
|
|
2
|
+
|
|
3
|
+
export const store = new Store({
|
|
4
|
+
firstName: 'Jane',
|
|
5
|
+
lastName: 'Smith',
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export const fullName = new Derived({
|
|
9
|
+
fn: () => `${store.state.firstName} ${store.state.lastName}`,
|
|
10
|
+
deps: [store],
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
fullName.mount()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/react-router'
|
|
2
|
+
import { useStore } from '@tanstack/react-store'
|
|
3
|
+
|
|
4
|
+
import { fullName, store } from '../lib/demo-store'
|
|
5
|
+
<% if (codeRouter) { %>
|
|
6
|
+
import type { RootRoute } from '@tanstack/react-router'
|
|
7
|
+
<% } else { %>
|
|
8
|
+
export const Route = createFileRoute('/demo/store')({
|
|
9
|
+
component: DemoStore,
|
|
10
|
+
})
|
|
11
|
+
<% } %>
|
|
12
|
+
|
|
13
|
+
function FirstName() {
|
|
14
|
+
const firstName = useStore(store, (state) => state.firstName)
|
|
15
|
+
return (
|
|
16
|
+
<input
|
|
17
|
+
type="text"
|
|
18
|
+
value={firstName}
|
|
19
|
+
onChange={(e) =>
|
|
20
|
+
store.setState((state) => ({ ...state, firstName: e.target.value }))
|
|
21
|
+
}
|
|
22
|
+
className="bg-white/10 rounded-lg px-4 py-2 outline-none border border-white/20 hover:border-white/40 focus:border-white/60 transition-colors duration-200 placeholder-white/40"
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function LastName() {
|
|
28
|
+
const lastName = useStore(store, (state) => state.lastName)
|
|
29
|
+
return (
|
|
30
|
+
<input
|
|
31
|
+
type="text"
|
|
32
|
+
value={lastName}
|
|
33
|
+
onChange={(e) =>
|
|
34
|
+
store.setState((state) => ({ ...state, lastName: e.target.value }))
|
|
35
|
+
}
|
|
36
|
+
className="bg-white/10 rounded-lg px-4 py-2 outline-none border border-white/20 hover:border-white/40 focus:border-white/60 transition-colors duration-200 placeholder-white/40"
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function FullName() {
|
|
42
|
+
const fName = useStore(fullName)
|
|
43
|
+
return (
|
|
44
|
+
<div className="bg-white/10 rounded-lg px-4 py-2 outline-none ">
|
|
45
|
+
{fName}
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function DemoStore() {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
className="min-h-[calc(100vh-32px)] text-white p-8 flex items-center justify-center w-full h-full"
|
|
54
|
+
style={{
|
|
55
|
+
backgroundImage:
|
|
56
|
+
'radial-gradient(50% 50% at 80% 80%, #f4a460 0%, #8b4513 70%, #1a0f0a 100%)',
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<div className="bg-white/10 backdrop-blur-lg rounded-xl p-8 shadow-lg flex flex-col gap-4 text-3xl min-w-1/2">
|
|
60
|
+
<h1 className="text-4xl font-bold mb-5">Store Example</h1>
|
|
61
|
+
<FirstName />
|
|
62
|
+
<LastName />
|
|
63
|
+
<FullName />
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
<% if (codeRouter) { %>
|
|
70
|
+
export default (parentRoute: RootRoute) => createRoute({
|
|
71
|
+
path: '/demo/store',
|
|
72
|
+
component: DemoStore,
|
|
73
|
+
getParentRoute: () => parentRoute,
|
|
74
|
+
})
|
|
75
|
+
<% } %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Store",
|
|
3
|
+
"description": "Add TanStack Store to your application.",
|
|
4
|
+
"phase": "add-on",
|
|
5
|
+
"link": "https://tanstack.com/store/latest",
|
|
6
|
+
"templates": ["file-router", "code-router"],
|
|
7
|
+
"routes": [
|
|
8
|
+
{
|
|
9
|
+
"url": "/demo/store",
|
|
10
|
+
"name": "Store"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
+
|
|
3
|
+
const queryClient = new QueryClient()
|
|
4
|
+
|
|
5
|
+
export default function Provider({ children }: { children: React.ReactNode }) {
|
|
6
|
+
return (
|
|
7
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
8
|
+
)
|
|
9
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/react-router'
|
|
2
|
+
import { useQuery } from '@tanstack/react-query'
|
|
3
|
+
<% if (codeRouter) { %>
|
|
4
|
+
import type { RootRoute } from '@tanstack/react-router'
|
|
5
|
+
<% } else { %>
|
|
6
|
+
export const Route = createFileRoute('/demo/tanstack-query')({
|
|
7
|
+
component: TanStackQueryDemo,
|
|
8
|
+
})
|
|
9
|
+
<% } %>
|
|
10
|
+
function TanStackQueryDemo() {
|
|
11
|
+
const { data } = useQuery({
|
|
12
|
+
queryKey: ['people'],
|
|
13
|
+
queryFn: () =>
|
|
14
|
+
fetch('https://swapi.dev/api/people')
|
|
15
|
+
.then((res) => res.json())
|
|
16
|
+
.then((data) => data.results as { name: string }[]),
|
|
17
|
+
initialData: [],
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="p-4">
|
|
22
|
+
<h1 className="text-2xl mb-4">People list from Swapi</h1>
|
|
23
|
+
<ul>
|
|
24
|
+
{data.map((person) => (
|
|
25
|
+
<li key={person.name}>{person.name}</li>
|
|
26
|
+
))}
|
|
27
|
+
</ul>
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
<% if (codeRouter) { %>
|
|
33
|
+
export default (parentRoute: RootRoute) => createRoute({
|
|
34
|
+
path: '/demo/tanstack-query',
|
|
35
|
+
component: TanStackQueryDemo,
|
|
36
|
+
getParentRoute: () => parentRoute,
|
|
37
|
+
})
|
|
38
|
+
<% } %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "TanStack Query",
|
|
3
|
+
"description": "Integrate TanStack Query into your application.",
|
|
4
|
+
"phase": "add-on",
|
|
5
|
+
"templates": ["file-router", "code-router"],
|
|
6
|
+
"link": "https://tanstack.com/query/latest",
|
|
7
|
+
"routes": [
|
|
8
|
+
{
|
|
9
|
+
"url": "/demo/tanstack-query",
|
|
10
|
+
"name": "TanStack Query"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -31,6 +31,11 @@ This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
|
|
|
31
31
|
<% } else { %>
|
|
32
32
|
This project uses CSS for styling.
|
|
33
33
|
<% } %>
|
|
34
|
+
|
|
35
|
+
<% for(const addon of addOns.filter(addon => addon.readme)) { %>
|
|
36
|
+
<%- addon.readme %>
|
|
37
|
+
<% } %>
|
|
38
|
+
|
|
34
39
|
## Routing
|
|
35
40
|
<% if (fileRouter) { %>This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as fiels in `src/routes`.<% } else { %>This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a code based router. Which means that the routes are defined in code (in the `./src/main.<%= jsx %>` file). If you like you can also use a file based routing setup by following the [File Based Routing](https://tanstack.com/router/latest/docs/framework/react/guide/file-based-routing) guide.<% } %>
|
|
36
41
|
|
|
@@ -525,6 +530,10 @@ Once we've created the derived store we can use it in the `App` component just l
|
|
|
525
530
|
|
|
526
531
|
You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).
|
|
527
532
|
|
|
533
|
+
# Demo files
|
|
534
|
+
|
|
535
|
+
Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
|
|
536
|
+
|
|
528
537
|
# Learn More
|
|
529
538
|
|
|
530
539
|
You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Link } from '@tanstack/react-router'
|
|
2
|
+
<% for(const integration of integrations.filter(i => i.type === 'header-user')) { %>
|
|
3
|
+
import <%= integration.name %> from "../<%= integration.path %>";
|
|
4
|
+
<% } %>
|
|
5
|
+
|
|
6
|
+
export default function Header() {
|
|
7
|
+
return (
|
|
8
|
+
<header className="p-2 flex gap-2 bg-white text-black justify-between">
|
|
9
|
+
<nav className="flex flex-row">
|
|
10
|
+
<div className="px-2 font-bold">
|
|
11
|
+
<Link to="/">Home</Link>
|
|
12
|
+
</div>
|
|
13
|
+
<% for(const addOn of addOns) {
|
|
14
|
+
for(const route of addOn?.routes || []) { %>
|
|
15
|
+
<div className="px-2 font-bold"><Link to="<%= route.url %>"><%= route.name %></Link></div>
|
|
16
|
+
<% } } %>
|
|
17
|
+
</nav>
|
|
18
|
+
<div>
|
|
19
|
+
<% for(const integration of integrations.filter(i => i.type === 'header-user')) { %>
|
|
20
|
+
<<%= integration.name %> />
|
|
21
|
+
<% } %>
|
|
22
|
+
</div>
|
|
23
|
+
</header>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"noUnusedLocals": true,
|
|
20
20
|
"noUnusedParameters": true,
|
|
21
21
|
"noFallthroughCasesInSwitch": true,
|
|
22
|
-
"noUncheckedSideEffectImports": true
|
|
22
|
+
"noUncheckedSideEffectImports": true<% if (addOnEnabled.shadcn) { %>,
|
|
23
|
+
"baseUrl": ".",
|
|
24
|
+
"paths": {
|
|
25
|
+
"@/*": ["./src/*"],
|
|
26
|
+
}<% } %>
|
|
23
27
|
}
|
|
24
28
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import viteReact from "@vitejs/plugin-react";<% if (tailwind) { %>
|
|
3
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
4
|
+
<% } %><%if (fileRouter) { %>
|
|
5
|
+
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";<% } %><% if (addOnEnabled['module-federation']) { %>
|
|
6
|
+
import {federation} from "@module-federation/vite";<% } %><% if (addOnEnabled.shadcn) { %>
|
|
7
|
+
import { resolve } from "path";<% } %><% if (addOnEnabled['module-federation']) { %>
|
|
8
|
+
import federationConfig from "./module-federation.config.js";
|
|
9
|
+
<% } %>
|
|
10
|
+
|
|
11
|
+
// https://vitejs.dev/config/
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
plugins: [<% if(fileRouter) { %>TanStackRouterVite({ autoCodeSplitting: true }), <% } %>viteReact()<% if (tailwind) { %>, tailwindcss()<% } %><% if (addOnEnabled['module-federation']) { %>, federation(federationConfig)<% } %>],
|
|
14
|
+
test: {
|
|
15
|
+
globals: true,
|
|
16
|
+
environment: "jsdom",
|
|
17
|
+
},
|
|
18
|
+
<% if (addOnEnabled.shadcn) { %> resolve: {
|
|
19
|
+
alias: {
|
|
20
|
+
'@': resolve(__dirname, './src'),
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
<% } %>
|
|
24
|
+
});
|
|
@@ -8,6 +8,12 @@ import {
|
|
|
8
8
|
createRouter,
|
|
9
9
|
} from "@tanstack/react-router";
|
|
10
10
|
import { TanStackRouterDevtools } from "@tanstack/router-devtools";
|
|
11
|
+
<% for(const route of routes) { %>import <%= route.name %> from "<%= route.path %>";
|
|
12
|
+
<% } %><% if (routes.length > 0) { %>
|
|
13
|
+
import Header from "./components/Header";
|
|
14
|
+
<% } %><% for(const integration of integrations.filter(i => i.type === 'layout' || i.type === 'provider')) { %>
|
|
15
|
+
import <%= integration.name %> from "./<%= integration.path %>";
|
|
16
|
+
<% } %>
|
|
11
17
|
|
|
12
18
|
import "./styles.css";
|
|
13
19
|
import reportWebVitals from "./reportWebVitals.<%= js %>";
|
|
@@ -17,8 +23,18 @@ import App from "./App.<%= jsx %>";
|
|
|
17
23
|
const rootRoute = createRootRoute({
|
|
18
24
|
component: () => (
|
|
19
25
|
<>
|
|
26
|
+
<% for(const integration of integrations.filter(i => i.type === 'provider')) { %>
|
|
27
|
+
<<%= integration.name %>>
|
|
28
|
+
<% } %>
|
|
29
|
+
<% if (routes.length > 0) { %><Header /><% } %>
|
|
20
30
|
<Outlet />
|
|
21
31
|
<TanStackRouterDevtools />
|
|
32
|
+
<% for(const integration of integrations.filter(i => i.type === 'layout')) { %>
|
|
33
|
+
<<%= integration.name %> />
|
|
34
|
+
<% } %>
|
|
35
|
+
<% for(const integration of integrations.filter(i => i.type === 'provider').toReversed()) { %>
|
|
36
|
+
</<%= integration.name %>>
|
|
37
|
+
<% } %>
|
|
22
38
|
</>
|
|
23
39
|
),
|
|
24
40
|
});
|
|
@@ -29,7 +45,7 @@ const indexRoute = createRoute({
|
|
|
29
45
|
component: App,
|
|
30
46
|
});
|
|
31
47
|
|
|
32
|
-
const routeTree = rootRoute.addChildren([indexRoute]);
|
|
48
|
+
const routeTree = rootRoute.addChildren([indexRoute<%= routes.map(route => `, ${route.name}(rootRoute)`).join('') %>]);
|
|
33
49
|
|
|
34
50
|
const router = createRouter({
|
|
35
51
|
routeTree,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# TanStack Chat Application
|
|
2
|
+
|
|
3
|
+
Am example chat application built with TanStack Start, TanStack Store, and Claude AI.
|
|
4
|
+
|
|
5
|
+
## .env Updates
|
|
6
|
+
|
|
7
|
+
```env
|
|
8
|
+
ANTHROPIC_API_KEY=your_anthropic_api_key
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## ✨ Features
|
|
12
|
+
|
|
13
|
+
### AI Capabilities
|
|
14
|
+
- 🤖 Powered by Claude 3.5 Sonnet
|
|
15
|
+
- 📝 Rich markdown formatting with syntax highlighting
|
|
16
|
+
- 🎯 Customizable system prompts for tailored AI behavior
|
|
17
|
+
- 🔄 Real-time message updates and streaming responses (coming soon)
|
|
18
|
+
|
|
19
|
+
### User Experience
|
|
20
|
+
- 🎨 Modern UI with Tailwind CSS and Lucide icons
|
|
21
|
+
- 🔍 Conversation management and history
|
|
22
|
+
- 🔐 Secure API key management
|
|
23
|
+
- 📋 Markdown rendering with code highlighting
|
|
24
|
+
|
|
25
|
+
### Technical Features
|
|
26
|
+
- 📦 Centralized state management with TanStack Store
|
|
27
|
+
- 🔌 Extensible architecture for multiple AI providers
|
|
28
|
+
- 🛠️ TypeScript for type safety
|
|
29
|
+
|
|
30
|
+
## Architecture
|
|
31
|
+
|
|
32
|
+
### Tech Stack
|
|
33
|
+
- **Frontend Framework**: TanStack Start
|
|
34
|
+
- **Routing**: TanStack Router
|
|
35
|
+
- **State Management**: TanStack Store
|
|
36
|
+
- **Styling**: Tailwind CSS
|
|
37
|
+
- **AI Integration**: Anthropic's Claude API
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { PlusCircle, Trash2 } from 'lucide-react'
|
|
3
|
+
import { useAppState } from '../store/demo.hooks'
|
|
4
|
+
|
|
5
|
+
interface SettingsDialogProps {
|
|
6
|
+
isOpen: boolean
|
|
7
|
+
onClose: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function SettingsDialog({ isOpen, onClose }: SettingsDialogProps) {
|
|
11
|
+
const [promptForm, setPromptForm] = useState({ name: '', content: '' })
|
|
12
|
+
const [isAddingPrompt, setIsAddingPrompt] = useState(false)
|
|
13
|
+
const { prompts, createPrompt, deletePrompt, setPromptActive } = useAppState()
|
|
14
|
+
|
|
15
|
+
const handleAddPrompt = () => {
|
|
16
|
+
if (!promptForm.name.trim() || !promptForm.content.trim()) return
|
|
17
|
+
createPrompt(promptForm.name, promptForm.content)
|
|
18
|
+
setPromptForm({ name: '', content: '' })
|
|
19
|
+
setIsAddingPrompt(false)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const handleClose = () => {
|
|
23
|
+
onClose()
|
|
24
|
+
setIsAddingPrompt(false)
|
|
25
|
+
setPromptForm({ name: '', content: '' })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!isOpen) return null
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center" onClick={(e) => {
|
|
32
|
+
if (e.target === e.currentTarget) handleClose()
|
|
33
|
+
}}>
|
|
34
|
+
<div className="bg-gray-800 rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto" onClick={e => e.stopPropagation()}>
|
|
35
|
+
<div className="p-6">
|
|
36
|
+
<div className="flex items-center justify-between mb-4">
|
|
37
|
+
<h2 className="text-2xl font-semibold text-white">Settings</h2>
|
|
38
|
+
<button
|
|
39
|
+
onClick={handleClose}
|
|
40
|
+
className="text-gray-400 hover:text-white focus:outline-none"
|
|
41
|
+
>
|
|
42
|
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
43
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
44
|
+
</svg>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div className="space-y-6">
|
|
49
|
+
{/* Prompts Management */}
|
|
50
|
+
<div className="space-y-2">
|
|
51
|
+
<div className="flex items-center justify-between mb-4">
|
|
52
|
+
<label className="block text-sm font-medium text-white">
|
|
53
|
+
System Prompts
|
|
54
|
+
</label>
|
|
55
|
+
<button
|
|
56
|
+
onClick={() => setIsAddingPrompt(true)}
|
|
57
|
+
className="flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-white bg-gradient-to-r from-orange-500 to-red-600 rounded-lg hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-orange-500"
|
|
58
|
+
>
|
|
59
|
+
<PlusCircle className="w-4 h-4" />
|
|
60
|
+
Add Prompt
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
{isAddingPrompt && (
|
|
65
|
+
<div className="space-y-3 mb-4 p-3 bg-gray-700/50 rounded-lg">
|
|
66
|
+
<input
|
|
67
|
+
type="text"
|
|
68
|
+
value={promptForm.name}
|
|
69
|
+
onChange={(e) => setPromptForm(prev => ({ ...prev, name: e.target.value }))}
|
|
70
|
+
placeholder="Prompt name..."
|
|
71
|
+
className="w-full px-3 py-2 text-sm text-white bg-gray-700 rounded-lg border border-gray-600 focus:border-orange-500 focus:ring-1 focus:ring-orange-500"
|
|
72
|
+
/>
|
|
73
|
+
<textarea
|
|
74
|
+
value={promptForm.content}
|
|
75
|
+
onChange={(e) => setPromptForm(prev => ({ ...prev, content: e.target.value }))}
|
|
76
|
+
placeholder="Enter prompt content..."
|
|
77
|
+
className="w-full h-32 px-3 py-2 text-sm text-white bg-gray-700 rounded-lg border border-gray-600 focus:border-orange-500 focus:ring-1 focus:ring-orange-500"
|
|
78
|
+
/>
|
|
79
|
+
<div className="flex justify-end gap-2">
|
|
80
|
+
<button
|
|
81
|
+
onClick={() => setIsAddingPrompt(false)}
|
|
82
|
+
className="px-3 py-1.5 text-sm font-medium text-gray-300 hover:text-white focus:outline-none"
|
|
83
|
+
>
|
|
84
|
+
Cancel
|
|
85
|
+
</button>
|
|
86
|
+
<button
|
|
87
|
+
onClick={handleAddPrompt}
|
|
88
|
+
className="px-3 py-1.5 text-sm font-medium text-white bg-gradient-to-r from-orange-500 to-red-600 rounded-lg hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-orange-500"
|
|
89
|
+
>
|
|
90
|
+
Save Prompt
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
|
|
96
|
+
<div className="space-y-2">
|
|
97
|
+
{prompts.map((prompt) => (
|
|
98
|
+
<div key={prompt.id} className="flex items-center justify-between p-3 bg-gray-700/50 rounded-lg">
|
|
99
|
+
<div className="flex-1 min-w-0 mr-4">
|
|
100
|
+
<h4 className="text-sm font-medium text-white truncate">{prompt.name}</h4>
|
|
101
|
+
<p className="text-xs text-gray-400 truncate">{prompt.content}</p>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="flex items-center gap-2">
|
|
104
|
+
<label className="relative inline-flex items-center cursor-pointer">
|
|
105
|
+
<input
|
|
106
|
+
type="checkbox"
|
|
107
|
+
className="sr-only peer"
|
|
108
|
+
checked={prompt.is_active}
|
|
109
|
+
onChange={() => setPromptActive(prompt.id, !prompt.is_active)}
|
|
110
|
+
/>
|
|
111
|
+
<div className="w-11 h-6 bg-gray-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-orange-500"></div>
|
|
112
|
+
</label>
|
|
113
|
+
<button
|
|
114
|
+
onClick={() => deletePrompt(prompt.id)}
|
|
115
|
+
className="p-1 text-gray-400 hover:text-red-500"
|
|
116
|
+
>
|
|
117
|
+
<Trash2 className="w-4 h-4" />
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
<p className="text-xs text-gray-400">
|
|
124
|
+
Create and manage custom system prompts. Only one prompt can be active at a time.
|
|
125
|
+
</p>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div className="mt-6 flex justify-end gap-3">
|
|
131
|
+
<button
|
|
132
|
+
onClick={handleClose}
|
|
133
|
+
className="px-4 py-2 text-sm font-medium text-gray-300 hover:text-white focus:outline-none"
|
|
134
|
+
>
|
|
135
|
+
Cancel
|
|
136
|
+
</button>
|
|
137
|
+
<button
|
|
138
|
+
onClick={handleClose}
|
|
139
|
+
className="px-4 py-2 text-sm font-medium text-white bg-gradient-to-r from-orange-500 to-red-600 rounded-lg hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-orange-500"
|
|
140
|
+
>
|
|
141
|
+
Close
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
)
|
|
148
|
+
}
|