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.
Files changed (165) hide show
  1. package/README.md +41 -9
  2. package/dist/add-ons.js +69 -0
  3. package/dist/cli.js +78 -0
  4. package/dist/constants.js +4 -0
  5. package/dist/create-app.js +371 -0
  6. package/dist/index.js +2 -347
  7. package/dist/mcp.js +169 -0
  8. package/dist/options.js +260 -0
  9. package/dist/{utils/getPackageManager.js → package-manager.js} +1 -0
  10. package/dist/types.js +1 -0
  11. package/package.json +6 -3
  12. package/src/add-ons.ts +156 -0
  13. package/src/cli.ts +114 -0
  14. package/src/constants.ts +7 -0
  15. package/src/create-app.ts +582 -0
  16. package/src/index.ts +2 -507
  17. package/src/mcp.ts +205 -0
  18. package/src/options.ts +308 -0
  19. package/src/{utils/getPackageManager.ts → package-manager.ts} +1 -0
  20. package/src/types.ts +30 -0
  21. package/templates/react/add-on/clerk/README.md +3 -0
  22. package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
  23. package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
  24. package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
  25. package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
  26. package/templates/react/add-on/clerk/info.json +13 -0
  27. package/templates/react/add-on/clerk/package.json +5 -0
  28. package/templates/react/add-on/convex/README.md +4 -0
  29. package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
  30. package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
  31. package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
  32. package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
  33. package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
  34. package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
  35. package/templates/react/add-on/convex/info.json +13 -0
  36. package/templates/react/add-on/convex/package.json +6 -0
  37. package/templates/react/add-on/form/assets/src/routes/demo.form.tsx.ejs +62 -0
  38. package/templates/react/add-on/form/info.json +13 -0
  39. package/templates/react/add-on/form/package.json +5 -0
  40. package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
  41. package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  42. package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
  43. package/templates/react/add-on/module-federation/info.json +7 -0
  44. package/templates/react/add-on/module-federation/package.json +5 -0
  45. package/templates/react/add-on/netlify/README.md +11 -0
  46. package/templates/react/add-on/netlify/info.json +7 -0
  47. package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  48. package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
  49. package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
  50. package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
  51. package/templates/react/add-on/sentry/info.json +14 -0
  52. package/templates/react/add-on/sentry/package.json +7 -0
  53. package/templates/react/add-on/shadcn/README.md +7 -0
  54. package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
  55. package/templates/react/add-on/shadcn/info.json +11 -0
  56. package/templates/react/add-on/start/assets/app.config.ts +16 -0
  57. package/templates/react/add-on/start/assets/postcss.config.ts +5 -0
  58. package/templates/react/add-on/start/assets/src/api.ts +6 -0
  59. package/templates/react/add-on/start/assets/src/client.tsx +10 -0
  60. package/templates/react/add-on/start/assets/src/router.tsx.ejs +51 -0
  61. package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
  62. package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
  63. package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
  64. package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
  65. package/templates/react/add-on/start/info.json +19 -0
  66. package/templates/react/add-on/start/package.json +14 -0
  67. package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
  68. package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
  69. package/templates/react/add-on/store/info.json +13 -0
  70. package/templates/react/add-on/store/package.json +6 -0
  71. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
  72. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +9 -0
  73. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +38 -0
  74. package/templates/react/add-on/tanstack-query/info.json +13 -0
  75. package/templates/react/add-on/tanstack-query/package.json +6 -0
  76. package/templates/{base → react/base}/README.md.ejs +9 -0
  77. package/templates/react/base/_dot_vscode/settings.json +11 -0
  78. package/templates/react/base/src/components/Header.tsx.ejs +25 -0
  79. package/templates/{base/tsconfig.json → react/base/tsconfig.json.ejs} +5 -1
  80. package/templates/react/base/vite.config.js.ejs +24 -0
  81. package/templates/{code-router → react/code-router}/src/main.tsx.ejs +17 -1
  82. package/templates/react/example/tanchat/README.md +37 -0
  83. package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
  84. package/templates/react/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +148 -0
  85. package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
  86. package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx.ejs +375 -0
  87. package/templates/react/example/tanchat/assets/src/store/demo.hooks.ts +21 -0
  88. package/templates/react/example/tanchat/assets/src/store/demo.store.ts +133 -0
  89. package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +108 -0
  90. package/templates/react/example/tanchat/info.json +15 -0
  91. package/templates/react/example/tanchat/package.json +10 -0
  92. package/templates/react/file-router/src/routes/__root.tsx.ejs +71 -0
  93. package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +148 -0
  94. package/templates/solid/add-on/form/info.json +13 -0
  95. package/templates/solid/add-on/form/package.json +5 -0
  96. package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
  97. package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
  98. package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
  99. package/templates/solid/add-on/module-federation/info.json +7 -0
  100. package/templates/solid/add-on/module-federation/package.json +5 -0
  101. package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
  102. package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
  103. package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
  104. package/templates/solid/add-on/sentry/info.json +13 -0
  105. package/templates/solid/add-on/sentry/package.json +5 -0
  106. package/templates/solid/add-on/solid-ui/README.md +9 -0
  107. package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
  108. package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
  109. package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
  110. package/templates/solid/add-on/solid-ui/info.json +11 -0
  111. package/templates/solid/add-on/solid-ui/package.json +9 -0
  112. package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
  113. package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
  114. package/templates/solid/add-on/store/info.json +13 -0
  115. package/templates/solid/add-on/store/package.json +6 -0
  116. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
  117. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
  118. package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
  119. package/templates/solid/add-on/tanstack-query/info.json +13 -0
  120. package/templates/solid/add-on/tanstack-query/package.json +6 -0
  121. package/templates/solid/base/README.md.ejs +200 -0
  122. package/templates/solid/base/_dot_cursorrules.append +35 -0
  123. package/templates/solid/base/_dot_gitignore +5 -0
  124. package/templates/solid/base/_dot_vscode/settings.json +11 -0
  125. package/templates/solid/base/index.html.ejs +20 -0
  126. package/templates/solid/base/package.json +22 -0
  127. package/templates/solid/base/package.ts.json +5 -0
  128. package/templates/solid/base/package.tw.json +6 -0
  129. package/templates/solid/base/public/favicon.ico +0 -0
  130. package/templates/solid/base/public/logo192.png +0 -0
  131. package/templates/solid/base/public/logo512.png +0 -0
  132. package/templates/solid/base/public/manifest.json +25 -0
  133. package/templates/solid/base/public/robots.txt +3 -0
  134. package/templates/solid/base/src/App.css +0 -0
  135. package/templates/solid/base/src/App.tsx.ejs +47 -0
  136. package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
  137. package/templates/solid/base/src/logo.svg +120 -0
  138. package/templates/solid/base/src/styles.css.ejs +15 -0
  139. package/templates/solid/base/tsconfig.json.ejs +30 -0
  140. package/templates/solid/base/vite.config.js.ejs +22 -0
  141. package/templates/solid/code-router/src/main.tsx.ejs +69 -0
  142. package/templates/solid/file-router/package.fr.json +5 -0
  143. package/templates/solid/file-router/src/main.tsx.ejs +44 -0
  144. package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
  145. package/templates/solid/file-router/src/routes/index.tsx +43 -0
  146. package/templates/base/vite.config.js.ejs +0 -15
  147. package/templates/file-router/src/routes/__root.tsx +0 -11
  148. /package/templates/{base/gitignore → react/base/_dot_gitignore} +0 -0
  149. /package/templates/{base → react/base}/index.html.ejs +0 -0
  150. /package/templates/{base → react/base}/package.json +0 -0
  151. /package/templates/{base → react/base}/package.ts.json +0 -0
  152. /package/templates/{base → react/base}/package.tw.json +0 -0
  153. /package/templates/{base → react/base}/public/favicon.ico +0 -0
  154. /package/templates/{base → react/base}/public/logo192.png +0 -0
  155. /package/templates/{base → react/base}/public/logo512.png +0 -0
  156. /package/templates/{base → react/base}/public/manifest.json +0 -0
  157. /package/templates/{base → react/base}/public/robots.txt +0 -0
  158. /package/templates/{base → react/base}/src/App.css +0 -0
  159. /package/templates/{base → react/base}/src/App.test.tsx.ejs +0 -0
  160. /package/templates/{base → react/base}/src/App.tsx.ejs +0 -0
  161. /package/templates/{base → react/base}/src/logo.svg +0 -0
  162. /package/templates/{base → react/base}/src/reportWebVitals.ts.ejs +0 -0
  163. /package/templates/{base → react/base}/src/styles.css.ejs +0 -0
  164. /package/templates/{file-router → react/file-router}/package.fr.json +0 -0
  165. /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
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "dependencies": {
3
+ "@tanstack/store": "^0.7.0",
4
+ "@tanstack/react-store": "^0.7.0"
5
+ }
6
+ }
@@ -0,0 +1,5 @@
1
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
2
+
3
+ export default function LayoutAddition() {
4
+ return <ReactQueryDevtools buttonPosition="bottom-right" />
5
+ }
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "dependencies": {
3
+ "@tanstack/react-query": "^5.66.5",
4
+ "@tanstack/react-query-devtools": "^5.66.5"
5
+ }
6
+ }
@@ -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,11 @@
1
+ {
2
+ "files.watcherExclude": {
3
+ "**/routeTree.gen.ts": true
4
+ },
5
+ "search.exclude": {
6
+ "**/routeTree.gen.ts": true
7
+ },
8
+ "files.readonlyInclude": {
9
+ "**/routeTree.gen.ts": true
10
+ }
11
+ }
@@ -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,2 @@
1
+ # Add your Anthropic API key
2
+ ANTHROPIC_API_KEY=
@@ -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
+ }