@tanstack/cta-framework-react-cra 0.45.0 → 0.46.2
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/add-ons/ai/assets/src/routes/demo/ai-chat.tsx +2 -2
- package/add-ons/prisma/assets/prisma/seed.ts.ejs +2 -2
- package/add-ons/prisma/assets/src/db.ts.ejs +2 -2
- package/add-ons/prisma/package.json.ejs +5 -5
- package/add-ons/sentry/package.json +2 -1
- package/add-ons/start/assets/vite.config.ts.ejs +7 -1
- package/examples/resume/README.md +82 -0
- package/examples/resume/assets/content/education/code-school.md +17 -0
- package/examples/resume/assets/content/jobs/freelance.md +13 -0
- package/examples/resume/assets/content/jobs/initech-junior.md +20 -0
- package/examples/resume/assets/content/jobs/initech-lead.md +29 -0
- package/examples/resume/assets/content/jobs/initrode-senior.md +28 -0
- package/examples/resume/assets/content-collections.ts +36 -0
- package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
- package/examples/resume/assets/src/components/ResumeAssistant.tsx +193 -0
- package/examples/resume/assets/src/components/ResumeAssistantButton.tsx +20 -0
- package/examples/resume/assets/src/components/ui/badge.tsx +46 -0
- package/examples/resume/assets/src/components/ui/card.tsx +92 -0
- package/examples/resume/assets/src/components/ui/checkbox.tsx +30 -0
- package/examples/resume/assets/src/components/ui/hover-card.tsx +44 -0
- package/examples/resume/assets/src/components/ui/separator.tsx +26 -0
- package/examples/resume/assets/src/lib/resume-ai-hook.ts +21 -0
- package/examples/resume/assets/src/lib/resume-tools.ts +165 -0
- package/examples/resume/assets/src/lib/utils.ts +6 -0
- package/examples/resume/assets/src/routes/api.resume-chat.ts +110 -0
- package/examples/resume/assets/src/routes/index.tsx +220 -0
- package/examples/resume/assets/src/styles.css +138 -0
- package/examples/resume/info.json +30 -0
- package/examples/resume/package.json +27 -0
- package/package.json +2 -2
- package/tests/snapshots/react-cra/cr-ts-start-apollo-client-npm.json +1 -1
- package/tests/snapshots/react-cra/cr-ts-start-npm.json +1 -1
- package/tests/snapshots/react-cra/cr-ts-start-tanstack-query-npm.json +1 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { useState, useMemo } from 'react'
|
|
2
|
+
import { marked } from 'marked'
|
|
3
|
+
|
|
4
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
5
|
+
import { allJobs, allEducations } from 'content-collections'
|
|
6
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
7
|
+
import { Checkbox } from '@/components/ui/checkbox'
|
|
8
|
+
import { Badge } from '@/components/ui/badge'
|
|
9
|
+
import { Separator } from '@/components/ui/separator'
|
|
10
|
+
import {
|
|
11
|
+
HoverCard,
|
|
12
|
+
HoverCardContent,
|
|
13
|
+
HoverCardTrigger,
|
|
14
|
+
} from '@/components/ui/hover-card'
|
|
15
|
+
|
|
16
|
+
import ResumeAssistant from '@/components/ResumeAssistant'
|
|
17
|
+
|
|
18
|
+
export const Route = createFileRoute('/')({
|
|
19
|
+
component: App,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
function App() {
|
|
23
|
+
const [selectedTags, setSelectedTags] = useState<string[]>([])
|
|
24
|
+
|
|
25
|
+
// Get unique tags from all jobs
|
|
26
|
+
const allTags = useMemo(() => {
|
|
27
|
+
const tags = new Set<string>()
|
|
28
|
+
allJobs.forEach((job) => {
|
|
29
|
+
job.tags.forEach((tag) => tags.add(tag))
|
|
30
|
+
})
|
|
31
|
+
return Array.from(tags).sort()
|
|
32
|
+
}, [])
|
|
33
|
+
|
|
34
|
+
// Filter jobs based on selected tags
|
|
35
|
+
const filteredJobs = useMemo(() => {
|
|
36
|
+
if (selectedTags.length === 0) return allJobs
|
|
37
|
+
return allJobs.filter((job) =>
|
|
38
|
+
selectedTags.some((tag) => job.tags.includes(tag)),
|
|
39
|
+
)
|
|
40
|
+
}, [selectedTags])
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<ResumeAssistant />
|
|
45
|
+
<div className="min-h-screen bg-linear-to-b from-gray-50 to-gray-100">
|
|
46
|
+
<div className="flex">
|
|
47
|
+
{/* Sidebar with filters */}
|
|
48
|
+
<div className="w-72 min-h-screen bg-white border-r shadow-sm p-8 sticky top-0">
|
|
49
|
+
<h3 className="text-lg font-semibold mb-6 text-gray-900">
|
|
50
|
+
Skills & Technologies
|
|
51
|
+
</h3>
|
|
52
|
+
<div className="space-y-4">
|
|
53
|
+
{allTags.map((tag) => (
|
|
54
|
+
<div key={tag} className="flex items-center space-x-3 group">
|
|
55
|
+
<Checkbox
|
|
56
|
+
id={tag}
|
|
57
|
+
checked={selectedTags.includes(tag)}
|
|
58
|
+
onCheckedChange={(checked) => {
|
|
59
|
+
if (checked) {
|
|
60
|
+
setSelectedTags([...selectedTags, tag])
|
|
61
|
+
} else {
|
|
62
|
+
setSelectedTags(selectedTags.filter((t) => t !== tag))
|
|
63
|
+
}
|
|
64
|
+
}}
|
|
65
|
+
className="data-[state=checked]:bg-blue-600"
|
|
66
|
+
/>
|
|
67
|
+
<label
|
|
68
|
+
htmlFor={tag}
|
|
69
|
+
className="text-sm font-medium leading-none text-gray-700 group-hover:text-gray-900 transition-colors cursor-pointer"
|
|
70
|
+
>
|
|
71
|
+
{tag}
|
|
72
|
+
</label>
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{/* Main content */}
|
|
79
|
+
<div className="flex-1 p-8 lg:p-12">
|
|
80
|
+
<div className="max-w-4xl mx-auto space-y-12">
|
|
81
|
+
<div className="text-center space-y-4">
|
|
82
|
+
<h1 className="text-5xl font-bold bg-linear-to-r from-gray-900 via-gray-800 to-gray-900 bg-clip-text text-transparent">
|
|
83
|
+
My Resume
|
|
84
|
+
</h1>
|
|
85
|
+
<p className="text-gray-600 text-lg">
|
|
86
|
+
Professional Experience & Education
|
|
87
|
+
</p>
|
|
88
|
+
<Separator className="mt-8" />
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Career Summary */}
|
|
92
|
+
<Card className="border-0 shadow-lg bg-white/50 backdrop-blur-sm">
|
|
93
|
+
<CardHeader>
|
|
94
|
+
<CardTitle className="text-2xl text-gray-900">
|
|
95
|
+
Career Summary
|
|
96
|
+
</CardTitle>
|
|
97
|
+
</CardHeader>
|
|
98
|
+
<CardContent>
|
|
99
|
+
<div className="flex items-center gap-8">
|
|
100
|
+
<p className="text-gray-700 flex-1 leading-relaxed">
|
|
101
|
+
I am a passionate and driven professional seeking
|
|
102
|
+
opportunities that will leverage my extensive experience
|
|
103
|
+
in frontend development while providing continuous growth
|
|
104
|
+
and learning opportunities. My goal is to contribute to
|
|
105
|
+
innovative projects that challenge me to expand my skill
|
|
106
|
+
set and make meaningful impacts through technology.
|
|
107
|
+
</p>
|
|
108
|
+
<img
|
|
109
|
+
src="/headshot-on-white.jpg"
|
|
110
|
+
alt="Professional headshot"
|
|
111
|
+
className="w-44 h-52 rounded-2xl object-cover shadow-md transition-transform hover:scale-105"
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
</CardContent>
|
|
115
|
+
</Card>
|
|
116
|
+
|
|
117
|
+
{/* Work Experience */}
|
|
118
|
+
<section className="space-y-6">
|
|
119
|
+
<h2 className="text-3xl font-semibold text-gray-900">
|
|
120
|
+
Work Experience
|
|
121
|
+
</h2>
|
|
122
|
+
<div className="space-y-6">
|
|
123
|
+
{filteredJobs.map((job) => (
|
|
124
|
+
<Card
|
|
125
|
+
key={job.jobTitle}
|
|
126
|
+
className="border-0 shadow-md hover:shadow-lg transition-shadow"
|
|
127
|
+
>
|
|
128
|
+
<CardHeader>
|
|
129
|
+
<div className="flex justify-between items-start">
|
|
130
|
+
<div className="space-y-2">
|
|
131
|
+
<CardTitle className="text-xl text-gray-900">
|
|
132
|
+
{job.jobTitle}
|
|
133
|
+
</CardTitle>
|
|
134
|
+
<p className="text-blue-600 font-medium">
|
|
135
|
+
{job.company} - {job.location}
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
<Badge variant="secondary" className="text-sm">
|
|
139
|
+
{job.startDate} - {job.endDate || 'Present'}
|
|
140
|
+
</Badge>
|
|
141
|
+
</div>
|
|
142
|
+
</CardHeader>
|
|
143
|
+
<CardContent>
|
|
144
|
+
<p className="text-gray-700 mb-6 leading-relaxed">
|
|
145
|
+
{job.summary}
|
|
146
|
+
</p>
|
|
147
|
+
<div className="flex flex-wrap gap-2">
|
|
148
|
+
{job.tags.map((tag) => (
|
|
149
|
+
<HoverCard key={tag}>
|
|
150
|
+
<HoverCardTrigger>
|
|
151
|
+
<Badge
|
|
152
|
+
variant="outline"
|
|
153
|
+
className="hover:bg-gray-100 transition-colors cursor-pointer"
|
|
154
|
+
>
|
|
155
|
+
{tag}
|
|
156
|
+
</Badge>
|
|
157
|
+
</HoverCardTrigger>
|
|
158
|
+
<HoverCardContent className="w-64">
|
|
159
|
+
<p className="text-sm text-gray-600">
|
|
160
|
+
Experience with {tag} in professional
|
|
161
|
+
development
|
|
162
|
+
</p>
|
|
163
|
+
</HoverCardContent>
|
|
164
|
+
</HoverCard>
|
|
165
|
+
))}
|
|
166
|
+
</div>
|
|
167
|
+
{job.content && (
|
|
168
|
+
<div
|
|
169
|
+
className="mt-6 text-gray-700 prose prose-sm max-w-none"
|
|
170
|
+
dangerouslySetInnerHTML={{
|
|
171
|
+
__html: marked(job.content),
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
)}
|
|
175
|
+
</CardContent>
|
|
176
|
+
</Card>
|
|
177
|
+
))}
|
|
178
|
+
</div>
|
|
179
|
+
</section>
|
|
180
|
+
|
|
181
|
+
{/* Education */}
|
|
182
|
+
<section className="space-y-6">
|
|
183
|
+
<h2 className="text-3xl font-semibold text-gray-900">
|
|
184
|
+
Education
|
|
185
|
+
</h2>
|
|
186
|
+
<div className="space-y-6">
|
|
187
|
+
{allEducations.map((education) => (
|
|
188
|
+
<Card
|
|
189
|
+
key={education.school}
|
|
190
|
+
className="border-0 shadow-md hover:shadow-lg transition-shadow"
|
|
191
|
+
>
|
|
192
|
+
<CardHeader>
|
|
193
|
+
<CardTitle className="text-xl text-gray-900">
|
|
194
|
+
{education.school}
|
|
195
|
+
</CardTitle>
|
|
196
|
+
</CardHeader>
|
|
197
|
+
<CardContent>
|
|
198
|
+
<p className="text-gray-700 leading-relaxed">
|
|
199
|
+
{education.summary}
|
|
200
|
+
</p>
|
|
201
|
+
{education.content && (
|
|
202
|
+
<div
|
|
203
|
+
className="mt-6 text-gray-700 prose prose-sm max-w-none"
|
|
204
|
+
dangerouslySetInnerHTML={{
|
|
205
|
+
__html: marked(education.content),
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
)}
|
|
209
|
+
</CardContent>
|
|
210
|
+
</Card>
|
|
211
|
+
))}
|
|
212
|
+
</div>
|
|
213
|
+
</section>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
</>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
|
|
3
|
+
@plugin "tailwindcss-animate";
|
|
4
|
+
|
|
5
|
+
@custom-variant dark (&:is(.dark *));
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
@apply m-0;
|
|
9
|
+
font-family:
|
|
10
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
|
11
|
+
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
code {
|
|
17
|
+
font-family:
|
|
18
|
+
source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
:root {
|
|
22
|
+
--background: oklch(1 0 0);
|
|
23
|
+
--foreground: oklch(0.141 0.005 285.823);
|
|
24
|
+
--card: oklch(1 0 0);
|
|
25
|
+
--card-foreground: oklch(0.141 0.005 285.823);
|
|
26
|
+
--popover: oklch(1 0 0);
|
|
27
|
+
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
28
|
+
--primary: oklch(0.21 0.006 285.885);
|
|
29
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
30
|
+
--secondary: oklch(0.967 0.001 286.375);
|
|
31
|
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
32
|
+
--muted: oklch(0.967 0.001 286.375);
|
|
33
|
+
--muted-foreground: oklch(0.552 0.016 285.938);
|
|
34
|
+
--accent: oklch(0.967 0.001 286.375);
|
|
35
|
+
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
36
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
37
|
+
--destructive-foreground: oklch(0.577 0.245 27.325);
|
|
38
|
+
--border: oklch(0.92 0.004 286.32);
|
|
39
|
+
--input: oklch(0.92 0.004 286.32);
|
|
40
|
+
--ring: oklch(0.871 0.006 286.286);
|
|
41
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
42
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
43
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
44
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
45
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
46
|
+
--radius: 0.625rem;
|
|
47
|
+
--sidebar: oklch(0.985 0 0);
|
|
48
|
+
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
|
49
|
+
--sidebar-primary: oklch(0.21 0.006 285.885);
|
|
50
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
51
|
+
--sidebar-accent: oklch(0.967 0.001 286.375);
|
|
52
|
+
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
|
53
|
+
--sidebar-border: oklch(0.92 0.004 286.32);
|
|
54
|
+
--sidebar-ring: oklch(0.871 0.006 286.286);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.dark {
|
|
58
|
+
--background: oklch(0.141 0.005 285.823);
|
|
59
|
+
--foreground: oklch(0.985 0 0);
|
|
60
|
+
--card: oklch(0.141 0.005 285.823);
|
|
61
|
+
--card-foreground: oklch(0.985 0 0);
|
|
62
|
+
--popover: oklch(0.141 0.005 285.823);
|
|
63
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
64
|
+
--primary: oklch(0.985 0 0);
|
|
65
|
+
--primary-foreground: oklch(0.21 0.006 285.885);
|
|
66
|
+
--secondary: oklch(0.274 0.006 286.033);
|
|
67
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
68
|
+
--muted: oklch(0.274 0.006 286.033);
|
|
69
|
+
--muted-foreground: oklch(0.705 0.015 286.067);
|
|
70
|
+
--accent: oklch(0.274 0.006 286.033);
|
|
71
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
72
|
+
--destructive: oklch(0.396 0.141 25.723);
|
|
73
|
+
--destructive-foreground: oklch(0.637 0.237 25.331);
|
|
74
|
+
--border: oklch(0.274 0.006 286.033);
|
|
75
|
+
--input: oklch(0.274 0.006 286.033);
|
|
76
|
+
--ring: oklch(0.442 0.017 285.786);
|
|
77
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
78
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
79
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
80
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
81
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
82
|
+
--sidebar: oklch(0.21 0.006 285.885);
|
|
83
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
84
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
85
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
86
|
+
--sidebar-accent: oklch(0.274 0.006 286.033);
|
|
87
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
88
|
+
--sidebar-border: oklch(0.274 0.006 286.033);
|
|
89
|
+
--sidebar-ring: oklch(0.442 0.017 285.786);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@theme inline {
|
|
93
|
+
--color-background: var(--background);
|
|
94
|
+
--color-foreground: var(--foreground);
|
|
95
|
+
--color-card: var(--card);
|
|
96
|
+
--color-card-foreground: var(--card-foreground);
|
|
97
|
+
--color-popover: var(--popover);
|
|
98
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
99
|
+
--color-primary: var(--primary);
|
|
100
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
101
|
+
--color-secondary: var(--secondary);
|
|
102
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
103
|
+
--color-muted: var(--muted);
|
|
104
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
105
|
+
--color-accent: var(--accent);
|
|
106
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
107
|
+
--color-destructive: var(--destructive);
|
|
108
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
109
|
+
--color-border: var(--border);
|
|
110
|
+
--color-input: var(--input);
|
|
111
|
+
--color-ring: var(--ring);
|
|
112
|
+
--color-chart-1: var(--chart-1);
|
|
113
|
+
--color-chart-2: var(--chart-2);
|
|
114
|
+
--color-chart-3: var(--chart-3);
|
|
115
|
+
--color-chart-4: var(--chart-4);
|
|
116
|
+
--color-chart-5: var(--chart-5);
|
|
117
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
118
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
119
|
+
--radius-lg: var(--radius);
|
|
120
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
121
|
+
--color-sidebar: var(--sidebar);
|
|
122
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
123
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
124
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
125
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
126
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
127
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
128
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@layer base {
|
|
132
|
+
* {
|
|
133
|
+
@apply border-border outline-ring/50;
|
|
134
|
+
}
|
|
135
|
+
body {
|
|
136
|
+
@apply bg-background text-foreground;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Resume",
|
|
3
|
+
"description": "Professional resume template with content-collections and shadcn UI components for Netlify.",
|
|
4
|
+
"phase": "example",
|
|
5
|
+
"modes": ["file-router"],
|
|
6
|
+
"type": "example",
|
|
7
|
+
"priority": 10,
|
|
8
|
+
"link": "",
|
|
9
|
+
"routes": [
|
|
10
|
+
{
|
|
11
|
+
"url": "/",
|
|
12
|
+
"path": "src/routes/index.tsx",
|
|
13
|
+
"jsName": "ResumeHome"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"integrations": [
|
|
17
|
+
{
|
|
18
|
+
"type": "header-user",
|
|
19
|
+
"path": "src/components/ResumeAssistantButton",
|
|
20
|
+
"jsName": "ResumeAssistantButton"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"type": "vite-plugin",
|
|
24
|
+
"import": "import contentCollections from '@content-collections/vite'",
|
|
25
|
+
"code": "contentCollections()"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"dependsOn": [],
|
|
29
|
+
"variables": []
|
|
30
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dependencies": {
|
|
3
|
+
"@radix-ui/react-checkbox": "^1.2.2",
|
|
4
|
+
"@radix-ui/react-hover-card": "^1.1.10",
|
|
5
|
+
"@radix-ui/react-separator": "^1.1.4",
|
|
6
|
+
"@radix-ui/react-slot": "^1.2.0",
|
|
7
|
+
"@tanstack/ai": "latest",
|
|
8
|
+
"@tanstack/ai-anthropic": "latest",
|
|
9
|
+
"@tanstack/ai-client": "latest",
|
|
10
|
+
"@tanstack/ai-gemini": "latest",
|
|
11
|
+
"@tanstack/ai-ollama": "latest",
|
|
12
|
+
"@tanstack/ai-openai": "latest",
|
|
13
|
+
"@tanstack/ai-react": "latest",
|
|
14
|
+
"@tanstack/store": "latest",
|
|
15
|
+
"class-variance-authority": "^0.7.1",
|
|
16
|
+
"clsx": "^2.1.1",
|
|
17
|
+
"marked": "^15.0.8",
|
|
18
|
+
"streamdown": "^1.6.5",
|
|
19
|
+
"tailwind-merge": "^3.0.2",
|
|
20
|
+
"tailwindcss-animate": "^1.0.7",
|
|
21
|
+
"zod": "^4.3.5"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@content-collections/core": "^0.13.1",
|
|
25
|
+
"@content-collections/vite": "^0.2.8"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/cta-framework-react-cra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.2",
|
|
4
4
|
"description": "CTA Framework for React (Create React App)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "Jack Herrington <jherr@pobox.com>",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@tanstack/cta-engine": "0.
|
|
26
|
+
"@tanstack/cta-engine": "0.46.2"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^24.6.0",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"README.md": "Welcome to your new TanStack app! \n\n# Getting Started\n\nTo run this application:\n\n```bash\nnpm install\nnpm run dev\n```\n\n# Building For Production\n\nTo build this application for production:\n\n```bash\nnpm run build\n```\n\n## Testing\n\nThis project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:\n\n```bash\nnpm run test\n```\n\n## Styling\n\nThis project uses [Tailwind CSS](https://tailwindcss.com/) for styling.\n\n\n\n# Apollo Client Integration\n\nThis add-on integrates Apollo Client with TanStack Start to provide modern streaming SSR support for GraphQL data fetching.\n\n## Dependencies\n\nThe following packages are automatically installed:\n\n- `@apollo/client` - Apollo Client core\n- `@apollo/client-integration-tanstack-start` - TanStack Start integration\n- `graphql` - GraphQL implementation\n\n## Configuration\n\n### 1. GraphQL Endpoint\n\nConfigure your GraphQL API endpoint in `src/router.tsx`:\n\n```tsx\n// Configure Apollo Client\nconst apolloClient = new ApolloClient({\n cache: new InMemoryCache(),\n link: new HttpLink({\n uri: 'https://your-graphql-api.example.com/graphql', // Update this!\n }),\n})\n```\n\nYou can use environment variables by creating a `.env.local` file:\n\n```bash\nVITE_GRAPHQL_ENDPOINT=https://your-api.com/graphql\n```\n\nThe default configuration already uses this pattern:\n\n```tsx\nuri: import.meta.env.VITE_GRAPHQL_ENDPOINT ||\n 'https://your-graphql-api.example.com/graphql'\n```\n\n## Usage Patterns\n\n### Pattern 1: Loader with preloadQuery (Recommended for SSR)\n\nUse `preloadQuery` in route loaders for optimal streaming SSR performance:\n\n```tsx\nimport { gql, TypedDocumentNode } from '@apollo/client'\nimport { useReadQuery } from '@apollo/client/react'\nimport { createFileRoute } from '@tanstack/react-router'\n\nconst MY_QUERY: TypedDocumentNode<{\n posts: { id: string; title: string; content: string }[]\n}> = gql`\n query GetData {\n posts {\n id\n title\n content\n }\n }\n`\n\nexport const Route = createFileRoute('/my-route')({\n component: RouteComponent,\n loader: ({ context: { preloadQuery } }) => {\n const queryRef = preloadQuery(MY_QUERY, {\n variables: {},\n })\n return { queryRef }\n },\n})\n\nfunction RouteComponent() {\n const { queryRef } = Route.useLoaderData()\n const { data } = useReadQuery(queryRef)\n\n return <div>{/* render your data */}</div>\n}\n```\n\n### Pattern 2: useSuspenseQuery\n\nUse `useSuspenseQuery` directly in components with automatic suspense support:\n\n```tsx\nimport { gql, TypedDocumentNode } from '@apollo/client'\nimport { useSuspenseQuery } from '@apollo/client/react'\nimport { createFileRoute } from '@tanstack/react-router'\n\nconst MY_QUERY: TypedDocumentNode<{\n posts: { id: string; title: string }[]\n}> = gql`\n query GetData {\n posts {\n id\n title\n }\n }\n`\n\nexport const Route = createFileRoute('/my-route')({\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { data } = useSuspenseQuery(MY_QUERY)\n\n return <div>{/* render your data */}</div>\n}\n```\n\n### Pattern 3: Manual Refetching\n\n```tsx\nimport { useQueryRefHandlers, useReadQuery } from '@apollo/client/react'\n\nfunction MyComponent() {\n const { queryRef } = Route.useLoaderData()\n const { refetch } = useQueryRefHandlers(queryRef)\n const { data } = useReadQuery(queryRef)\n\n return (\n <div>\n <button onClick={() => refetch()}>Refresh</button>\n {/* render data */}\n </div>\n )\n}\n```\n\n## Important Notes\n\n### SSR Optimization\n\nThe integration automatically handles:\n\n- Query deduplication across server and client\n- Streaming SSR with `@defer` directive support\n- Proper cache hydration\n\n## Learn More\n\n- [Apollo Client Documentation](https://www.apollographql.com/docs/react)\n- [@apollo/client-integration-tanstack-start](https://www.npmjs.com/package/@apollo/client-integration-tanstack-start)\n\n## Demo\n\nVisit `/demo/apollo-client` in your application to see a working example of Apollo Client integration.\n\n\n\n## Routing\nThis project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.\n\n### Adding A Route\n\nTo add a new route to your application just add another a new file in the `./src/routes` directory.\n\nTanStack will automatically generate the content of the route file for you.\n\nNow that you have two routes you can use a `Link` component to navigate between them.\n\n### Adding Links\n\nTo use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.\n\n```tsx\nimport { Link } from \"@tanstack/react-router\";\n```\n\nThen anywhere in your JSX you can use it like so:\n\n```tsx\n<Link to=\"/about\">About</Link>\n```\n\nThis will create a link that will navigate to the `/about` route.\n\nMore information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).\n\n### Using A Layout\n\nIn the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `<Outlet />` component.\n\nHere is an example layout that includes a header:\n\n```tsx\nimport { Outlet, createRootRoute } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nimport { Link } from \"@tanstack/react-router\";\n\nexport const Route = createRootRoute({\n component: () => (\n <>\n <header>\n <nav>\n <Link to=\"/\">Home</Link>\n <Link to=\"/about\">About</Link>\n </nav>\n </header>\n <Outlet />\n <TanStackRouterDevtools />\n </>\n ),\n})\n```\n\nThe `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout.\n\nMore information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).\n\n\n## Data Fetching\n\nThere are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.\n\nFor example:\n\n```tsx\nconst peopleRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: \"/people\",\n loader: async () => {\n const response = await fetch(\"https://swapi.dev/api/people\");\n return response.json() as Promise<{\n results: {\n name: string;\n }[];\n }>;\n },\n component: () => {\n const data = peopleRoute.useLoaderData();\n return (\n <ul>\n {data.results.map((person) => (\n <li key={person.name}>{person.name}</li>\n ))}\n </ul>\n );\n },\n});\n```\n\nLoaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).\n\n### React-Query\n\nReact-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.\n\nFirst add your dependencies:\n\n```bash\nnpm install @tanstack/react-query @tanstack/react-query-devtools\n```\n\nNext we'll need to create a query client and provider. We recommend putting those in `main.tsx`.\n\n```tsx\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\n\n// ...\n\nconst queryClient = new QueryClient();\n\n// ...\n\nif (!rootElement.innerHTML) {\n const root = ReactDOM.createRoot(rootElement);\n\n root.render(\n <QueryClientProvider client={queryClient}>\n <RouterProvider router={router} />\n </QueryClientProvider>\n );\n}\n```\n\nYou can also add TanStack Query Devtools to the root route (optional).\n\n```tsx\nimport { ReactQueryDevtools } from \"@tanstack/react-query-devtools\";\n\nconst rootRoute = createRootRoute({\n component: () => (\n <>\n <Outlet />\n <ReactQueryDevtools buttonPosition=\"top-right\" />\n <TanStackRouterDevtools />\n </>\n ),\n});\n```\n\nNow you can use `useQuery` to fetch your data.\n\n```tsx\nimport { useQuery } from \"@tanstack/react-query\";\n\nimport \"./App.css\";\n\nfunction App() {\n const { data } = useQuery({\n queryKey: [\"people\"],\n queryFn: () =>\n fetch(\"https://swapi.dev/api/people\")\n .then((res) => res.json())\n .then((data) => data.results as { name: string }[]),\n initialData: [],\n });\n\n return (\n <div>\n <ul>\n {data.map((person) => (\n <li key={person.name}>{person.name}</li>\n ))}\n </ul>\n </div>\n );\n}\n\nexport default App;\n```\n\nYou can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).\n\n## State Management\n\nAnother common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.\n\nFirst you need to add TanStack Store as a dependency:\n\n```bash\nnpm install @tanstack/store\n```\n\nNow let's create a simple counter in the `src/App.tsx` file as a demonstration.\n\n```tsx\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store } from \"@tanstack/store\";\nimport \"./App.css\";\n\nconst countStore = new Store(0);\n\nfunction App() {\n const count = useStore(countStore);\n return (\n <div>\n <button onClick={() => countStore.setState((n) => n + 1)}>\n Increment - {count}\n </button>\n </div>\n );\n}\n\nexport default App;\n```\n\nOne of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.\n\nLet's check this out by doubling the count using derived state.\n\n```tsx\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store, Derived } from \"@tanstack/store\";\nimport \"./App.css\";\n\nconst countStore = new Store(0);\n\nconst doubledStore = new Derived({\n fn: () => countStore.state * 2,\n deps: [countStore],\n});\ndoubledStore.mount();\n\nfunction App() {\n const count = useStore(countStore);\n const doubledCount = useStore(doubledStore);\n\n return (\n <div>\n <button onClick={() => countStore.setState((n) => n + 1)}>\n Increment - {count}\n </button>\n <div>Doubled - {doubledCount}</div>\n </div>\n );\n}\n\nexport default App;\n```\n\nWe use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.\n\nOnce we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.\n\nYou can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).\n\n# Demo files\n\nFiles 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.\n\n# Learn More\n\nYou can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).\n",
|
|
23
23
|
"package.json": "{\n \"name\": \"TEST\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite dev --port 3000\",\n \"build\": \"vite build\",\n \"preview\": \"vite preview\",\n \"test\": \"vitest run\"\n },\n \"dependencies\": {\n \"@apollo/client\": \"^4.0.0\",\n \"@apollo/client-integration-tanstack-start\": \"^0.14.2-rc.0\",\n \"@tailwindcss/vite\": \"^4.0.6\",\n \"@tanstack/react-devtools\": \"^0.7.0\",\n \"@tanstack/react-router\": \"^1.132.0\",\n \"@tanstack/react-router-devtools\": \"^1.132.0\",\n \"@tanstack/react-router-ssr-query\": \"^1.131.7\",\n \"@tanstack/react-start\": \"^1.132.0\",\n \"@tanstack/router-plugin\": \"^1.132.0\",\n \"graphql\": \"^16.10.0\",\n \"lucide-react\": \"^0.561.0\",\n \"react\": \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n \"rxjs\": \"^7.8.2\",\n \"tailwindcss\": \"^4.0.6\",\n \"vite-tsconfig-paths\": \"^6.0.2\"\n },\n \"devDependencies\": {\n \"@tanstack/devtools-vite\": \"^0.3.11\",\n \"@testing-library/dom\": \"^10.4.0\",\n \"@testing-library/react\": \"^16.2.0\",\n \"@types/node\": \"^22.10.2\",\n \"@types/react\": \"^19.2.0\",\n \"@types/react-dom\": \"^19.2.0\",\n \"@vitejs/plugin-react\": \"^5.0.4\",\n \"jsdom\": \"^27.0.0\",\n \"typescript\": \"^5.7.2\",\n \"vite\": \"^7.1.7\",\n \"vitest\": \"^3.0.5\",\n \"web-vitals\": \"^5.1.0\"\n }\n}",
|
|
24
24
|
"tsconfig.json": "{\n \"include\": [\"**/*.ts\", \"**/*.tsx\"],\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"jsx\": \"react-jsx\",\n \"module\": \"ESNext\",\n \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n \"types\": [\"vite/client\"],\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": false,\n \"noEmit\": true,\n\n /* Linting */\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUncheckedSideEffectImports\": true,\n \"baseUrl\": \".\",\n \"paths\": {\n \"@/*\": [\"./src/*\"]\n }\n }\n}\n",
|
|
25
|
-
"vite.config.ts": "import { defineConfig } from 'vite'\nimport { devtools } from '@tanstack/devtools-vite'\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite'\nimport viteReact from '@vitejs/plugin-react'\nimport viteTsConfigPaths from 'vite-tsconfig-paths'\nimport tailwindcss from '@tailwindcss/vite'\n\nconst config = defineConfig({\n plugins: [\n devtools(),\n // this is the plugin that enables path aliases\n viteTsConfigPaths({\n projects: ['./tsconfig.json'],\n }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n})\n\nexport default config\n"
|
|
25
|
+
"vite.config.ts": "import { defineConfig } from 'vite'\nimport { devtools } from '@tanstack/devtools-vite'\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite'\nimport viteReact from '@vitejs/plugin-react'\nimport viteTsConfigPaths from 'vite-tsconfig-paths'\nimport { fileURLToPath, URL } from 'url'\nimport tailwindcss from '@tailwindcss/vite'\n\nconst config = defineConfig({\n resolve: {\n alias: {\n '@': fileURLToPath(new URL('./src', import.meta.url)),\n },\n },\n plugins: [\n devtools(),\n // this is the plugin that enables path aliases\n viteTsConfigPaths({\n projects: ['./tsconfig.json'],\n }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n})\n\nexport default config\n"
|
|
26
26
|
},
|
|
27
27
|
"commands": [
|
|
28
28
|
"git init",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"README.md": "Welcome to your new TanStack app! \n\n# Getting Started\n\nTo run this application:\n\n```bash\nnpm install\nnpm run dev\n```\n\n# Building For Production\n\nTo build this application for production:\n\n```bash\nnpm run build\n```\n\n## Testing\n\nThis project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:\n\n```bash\nnpm run test\n```\n\n## Styling\n\nThis project uses [Tailwind CSS](https://tailwindcss.com/) for styling.\n\n\n\n\n## Routing\nThis project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.\n\n### Adding A Route\n\nTo add a new route to your application just add another a new file in the `./src/routes` directory.\n\nTanStack will automatically generate the content of the route file for you.\n\nNow that you have two routes you can use a `Link` component to navigate between them.\n\n### Adding Links\n\nTo use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.\n\n```tsx\nimport { Link } from \"@tanstack/react-router\";\n```\n\nThen anywhere in your JSX you can use it like so:\n\n```tsx\n<Link to=\"/about\">About</Link>\n```\n\nThis will create a link that will navigate to the `/about` route.\n\nMore information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).\n\n### Using A Layout\n\nIn the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `<Outlet />` component.\n\nHere is an example layout that includes a header:\n\n```tsx\nimport { Outlet, createRootRoute } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nimport { Link } from \"@tanstack/react-router\";\n\nexport const Route = createRootRoute({\n component: () => (\n <>\n <header>\n <nav>\n <Link to=\"/\">Home</Link>\n <Link to=\"/about\">About</Link>\n </nav>\n </header>\n <Outlet />\n <TanStackRouterDevtools />\n </>\n ),\n})\n```\n\nThe `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout.\n\nMore information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).\n\n\n## Data Fetching\n\nThere are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.\n\nFor example:\n\n```tsx\nconst peopleRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: \"/people\",\n loader: async () => {\n const response = await fetch(\"https://swapi.dev/api/people\");\n return response.json() as Promise<{\n results: {\n name: string;\n }[];\n }>;\n },\n component: () => {\n const data = peopleRoute.useLoaderData();\n return (\n <ul>\n {data.results.map((person) => (\n <li key={person.name}>{person.name}</li>\n ))}\n </ul>\n );\n },\n});\n```\n\nLoaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).\n\n### React-Query\n\nReact-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.\n\nFirst add your dependencies:\n\n```bash\nnpm install @tanstack/react-query @tanstack/react-query-devtools\n```\n\nNext we'll need to create a query client and provider. We recommend putting those in `main.tsx`.\n\n```tsx\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\n\n// ...\n\nconst queryClient = new QueryClient();\n\n// ...\n\nif (!rootElement.innerHTML) {\n const root = ReactDOM.createRoot(rootElement);\n\n root.render(\n <QueryClientProvider client={queryClient}>\n <RouterProvider router={router} />\n </QueryClientProvider>\n );\n}\n```\n\nYou can also add TanStack Query Devtools to the root route (optional).\n\n```tsx\nimport { ReactQueryDevtools } from \"@tanstack/react-query-devtools\";\n\nconst rootRoute = createRootRoute({\n component: () => (\n <>\n <Outlet />\n <ReactQueryDevtools buttonPosition=\"top-right\" />\n <TanStackRouterDevtools />\n </>\n ),\n});\n```\n\nNow you can use `useQuery` to fetch your data.\n\n```tsx\nimport { useQuery } from \"@tanstack/react-query\";\n\nimport \"./App.css\";\n\nfunction App() {\n const { data } = useQuery({\n queryKey: [\"people\"],\n queryFn: () =>\n fetch(\"https://swapi.dev/api/people\")\n .then((res) => res.json())\n .then((data) => data.results as { name: string }[]),\n initialData: [],\n });\n\n return (\n <div>\n <ul>\n {data.map((person) => (\n <li key={person.name}>{person.name}</li>\n ))}\n </ul>\n </div>\n );\n}\n\nexport default App;\n```\n\nYou can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).\n\n## State Management\n\nAnother common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.\n\nFirst you need to add TanStack Store as a dependency:\n\n```bash\nnpm install @tanstack/store\n```\n\nNow let's create a simple counter in the `src/App.tsx` file as a demonstration.\n\n```tsx\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store } from \"@tanstack/store\";\nimport \"./App.css\";\n\nconst countStore = new Store(0);\n\nfunction App() {\n const count = useStore(countStore);\n return (\n <div>\n <button onClick={() => countStore.setState((n) => n + 1)}>\n Increment - {count}\n </button>\n </div>\n );\n}\n\nexport default App;\n```\n\nOne of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.\n\nLet's check this out by doubling the count using derived state.\n\n```tsx\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store, Derived } from \"@tanstack/store\";\nimport \"./App.css\";\n\nconst countStore = new Store(0);\n\nconst doubledStore = new Derived({\n fn: () => countStore.state * 2,\n deps: [countStore],\n});\ndoubledStore.mount();\n\nfunction App() {\n const count = useStore(countStore);\n const doubledCount = useStore(doubledStore);\n\n return (\n <div>\n <button onClick={() => countStore.setState((n) => n + 1)}>\n Increment - {count}\n </button>\n <div>Doubled - {doubledCount}</div>\n </div>\n );\n}\n\nexport default App;\n```\n\nWe use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.\n\nOnce we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.\n\nYou can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).\n\n# Demo files\n\nFiles 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.\n\n# Learn More\n\nYou can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).\n",
|
|
22
22
|
"package.json": "{\n \"name\": \"TEST\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite dev --port 3000\",\n \"build\": \"vite build\",\n \"preview\": \"vite preview\",\n \"test\": \"vitest run\"\n },\n \"dependencies\": {\n \"@tailwindcss/vite\": \"^4.0.6\",\n \"@tanstack/react-devtools\": \"^0.7.0\",\n \"@tanstack/react-router\": \"^1.132.0\",\n \"@tanstack/react-router-devtools\": \"^1.132.0\",\n \"@tanstack/react-router-ssr-query\": \"^1.131.7\",\n \"@tanstack/react-start\": \"^1.132.0\",\n \"@tanstack/router-plugin\": \"^1.132.0\",\n \"lucide-react\": \"^0.561.0\",\n \"react\": \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n \"tailwindcss\": \"^4.0.6\",\n \"vite-tsconfig-paths\": \"^6.0.2\"\n },\n \"devDependencies\": {\n \"@tanstack/devtools-vite\": \"^0.3.11\",\n \"@testing-library/dom\": \"^10.4.0\",\n \"@testing-library/react\": \"^16.2.0\",\n \"@types/node\": \"^22.10.2\",\n \"@types/react\": \"^19.2.0\",\n \"@types/react-dom\": \"^19.2.0\",\n \"@vitejs/plugin-react\": \"^5.0.4\",\n \"jsdom\": \"^27.0.0\",\n \"typescript\": \"^5.7.2\",\n \"vite\": \"^7.1.7\",\n \"vitest\": \"^3.0.5\",\n \"web-vitals\": \"^5.1.0\"\n }\n}",
|
|
23
23
|
"tsconfig.json": "{\n \"include\": [\"**/*.ts\", \"**/*.tsx\"],\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"jsx\": \"react-jsx\",\n \"module\": \"ESNext\",\n \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n \"types\": [\"vite/client\"],\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": false,\n \"noEmit\": true,\n\n /* Linting */\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUncheckedSideEffectImports\": true,\n \"baseUrl\": \".\",\n \"paths\": {\n \"@/*\": [\"./src/*\"]\n }\n }\n}\n",
|
|
24
|
-
"vite.config.ts": "import { defineConfig } from 'vite'\nimport { devtools } from '@tanstack/devtools-vite'\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite'\nimport viteReact from '@vitejs/plugin-react'\nimport viteTsConfigPaths from 'vite-tsconfig-paths'\nimport tailwindcss from '@tailwindcss/vite'\n\nconst config = defineConfig({\n plugins: [\n devtools(),\n // this is the plugin that enables path aliases\n viteTsConfigPaths({\n projects: ['./tsconfig.json'],\n }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n})\n\nexport default config\n"
|
|
24
|
+
"vite.config.ts": "import { defineConfig } from 'vite'\nimport { devtools } from '@tanstack/devtools-vite'\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite'\nimport viteReact from '@vitejs/plugin-react'\nimport viteTsConfigPaths from 'vite-tsconfig-paths'\nimport { fileURLToPath, URL } from 'url'\nimport tailwindcss from '@tailwindcss/vite'\n\nconst config = defineConfig({\n resolve: {\n alias: {\n '@': fileURLToPath(new URL('./src', import.meta.url)),\n },\n },\n plugins: [\n devtools(),\n // this is the plugin that enables path aliases\n viteTsConfigPaths({\n projects: ['./tsconfig.json'],\n }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n})\n\nexport default config\n"
|
|
25
25
|
},
|
|
26
26
|
"commands": [
|
|
27
27
|
"git init",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"README.md": "Welcome to your new TanStack app! \n\n# Getting Started\n\nTo run this application:\n\n```bash\nnpm install\nnpm run dev\n```\n\n# Building For Production\n\nTo build this application for production:\n\n```bash\nnpm run build\n```\n\n## Testing\n\nThis project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:\n\n```bash\nnpm run test\n```\n\n## Styling\n\nThis project uses [Tailwind CSS](https://tailwindcss.com/) for styling.\n\n\n\n\n## Routing\nThis project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.\n\n### Adding A Route\n\nTo add a new route to your application just add another a new file in the `./src/routes` directory.\n\nTanStack will automatically generate the content of the route file for you.\n\nNow that you have two routes you can use a `Link` component to navigate between them.\n\n### Adding Links\n\nTo use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.\n\n```tsx\nimport { Link } from \"@tanstack/react-router\";\n```\n\nThen anywhere in your JSX you can use it like so:\n\n```tsx\n<Link to=\"/about\">About</Link>\n```\n\nThis will create a link that will navigate to the `/about` route.\n\nMore information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).\n\n### Using A Layout\n\nIn the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `<Outlet />` component.\n\nHere is an example layout that includes a header:\n\n```tsx\nimport { Outlet, createRootRoute } from '@tanstack/react-router'\nimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'\n\nimport { Link } from \"@tanstack/react-router\";\n\nexport const Route = createRootRoute({\n component: () => (\n <>\n <header>\n <nav>\n <Link to=\"/\">Home</Link>\n <Link to=\"/about\">About</Link>\n </nav>\n </header>\n <Outlet />\n <TanStackRouterDevtools />\n </>\n ),\n})\n```\n\nThe `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout.\n\nMore information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).\n\n\n## Data Fetching\n\nThere are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.\n\nFor example:\n\n```tsx\nconst peopleRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: \"/people\",\n loader: async () => {\n const response = await fetch(\"https://swapi.dev/api/people\");\n return response.json() as Promise<{\n results: {\n name: string;\n }[];\n }>;\n },\n component: () => {\n const data = peopleRoute.useLoaderData();\n return (\n <ul>\n {data.results.map((person) => (\n <li key={person.name}>{person.name}</li>\n ))}\n </ul>\n );\n },\n});\n```\n\nLoaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).\n\n### React-Query\n\nReact-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.\n\nFirst add your dependencies:\n\n```bash\nnpm install @tanstack/react-query @tanstack/react-query-devtools\n```\n\nNext we'll need to create a query client and provider. We recommend putting those in `main.tsx`.\n\n```tsx\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\n\n// ...\n\nconst queryClient = new QueryClient();\n\n// ...\n\nif (!rootElement.innerHTML) {\n const root = ReactDOM.createRoot(rootElement);\n\n root.render(\n <QueryClientProvider client={queryClient}>\n <RouterProvider router={router} />\n </QueryClientProvider>\n );\n}\n```\n\nYou can also add TanStack Query Devtools to the root route (optional).\n\n```tsx\nimport { ReactQueryDevtools } from \"@tanstack/react-query-devtools\";\n\nconst rootRoute = createRootRoute({\n component: () => (\n <>\n <Outlet />\n <ReactQueryDevtools buttonPosition=\"top-right\" />\n <TanStackRouterDevtools />\n </>\n ),\n});\n```\n\nNow you can use `useQuery` to fetch your data.\n\n```tsx\nimport { useQuery } from \"@tanstack/react-query\";\n\nimport \"./App.css\";\n\nfunction App() {\n const { data } = useQuery({\n queryKey: [\"people\"],\n queryFn: () =>\n fetch(\"https://swapi.dev/api/people\")\n .then((res) => res.json())\n .then((data) => data.results as { name: string }[]),\n initialData: [],\n });\n\n return (\n <div>\n <ul>\n {data.map((person) => (\n <li key={person.name}>{person.name}</li>\n ))}\n </ul>\n </div>\n );\n}\n\nexport default App;\n```\n\nYou can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).\n\n## State Management\n\nAnother common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.\n\nFirst you need to add TanStack Store as a dependency:\n\n```bash\nnpm install @tanstack/store\n```\n\nNow let's create a simple counter in the `src/App.tsx` file as a demonstration.\n\n```tsx\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store } from \"@tanstack/store\";\nimport \"./App.css\";\n\nconst countStore = new Store(0);\n\nfunction App() {\n const count = useStore(countStore);\n return (\n <div>\n <button onClick={() => countStore.setState((n) => n + 1)}>\n Increment - {count}\n </button>\n </div>\n );\n}\n\nexport default App;\n```\n\nOne of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.\n\nLet's check this out by doubling the count using derived state.\n\n```tsx\nimport { useStore } from \"@tanstack/react-store\";\nimport { Store, Derived } from \"@tanstack/store\";\nimport \"./App.css\";\n\nconst countStore = new Store(0);\n\nconst doubledStore = new Derived({\n fn: () => countStore.state * 2,\n deps: [countStore],\n});\ndoubledStore.mount();\n\nfunction App() {\n const count = useStore(countStore);\n const doubledCount = useStore(doubledStore);\n\n return (\n <div>\n <button onClick={() => countStore.setState((n) => n + 1)}>\n Increment - {count}\n </button>\n <div>Doubled - {doubledCount}</div>\n </div>\n );\n}\n\nexport default App;\n```\n\nWe use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.\n\nOnce we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.\n\nYou can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).\n\n# Demo files\n\nFiles 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.\n\n# Learn More\n\nYou can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).\n",
|
|
26
26
|
"package.json": "{\n \"name\": \"TEST\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite dev --port 3000\",\n \"build\": \"vite build\",\n \"preview\": \"vite preview\",\n \"test\": \"vitest run\"\n },\n \"dependencies\": {\n \"@tailwindcss/vite\": \"^4.0.6\",\n \"@tanstack/react-devtools\": \"^0.7.0\",\n \"@tanstack/react-query\": \"^5.66.5\",\n \"@tanstack/react-query-devtools\": \"^5.84.2\",\n \"@tanstack/react-router\": \"^1.132.0\",\n \"@tanstack/react-router-devtools\": \"^1.132.0\",\n \"@tanstack/react-router-ssr-query\": \"^1.131.7\",\n \"@tanstack/react-start\": \"^1.132.0\",\n \"@tanstack/router-plugin\": \"^1.132.0\",\n \"lucide-react\": \"^0.561.0\",\n \"react\": \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n \"tailwindcss\": \"^4.0.6\",\n \"vite-tsconfig-paths\": \"^6.0.2\"\n },\n \"devDependencies\": {\n \"@tanstack/devtools-vite\": \"^0.3.11\",\n \"@testing-library/dom\": \"^10.4.0\",\n \"@testing-library/react\": \"^16.2.0\",\n \"@types/node\": \"^22.10.2\",\n \"@types/react\": \"^19.2.0\",\n \"@types/react-dom\": \"^19.2.0\",\n \"@vitejs/plugin-react\": \"^5.0.4\",\n \"jsdom\": \"^27.0.0\",\n \"typescript\": \"^5.7.2\",\n \"vite\": \"^7.1.7\",\n \"vitest\": \"^3.0.5\",\n \"web-vitals\": \"^5.1.0\"\n }\n}",
|
|
27
27
|
"tsconfig.json": "{\n \"include\": [\"**/*.ts\", \"**/*.tsx\"],\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"jsx\": \"react-jsx\",\n \"module\": \"ESNext\",\n \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n \"types\": [\"vite/client\"],\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": false,\n \"noEmit\": true,\n\n /* Linting */\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUncheckedSideEffectImports\": true,\n \"baseUrl\": \".\",\n \"paths\": {\n \"@/*\": [\"./src/*\"]\n }\n }\n}\n",
|
|
28
|
-
"vite.config.ts": "import { defineConfig } from 'vite'\nimport { devtools } from '@tanstack/devtools-vite'\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite'\nimport viteReact from '@vitejs/plugin-react'\nimport viteTsConfigPaths from 'vite-tsconfig-paths'\nimport tailwindcss from '@tailwindcss/vite'\n\nconst config = defineConfig({\n plugins: [\n devtools(),\n // this is the plugin that enables path aliases\n viteTsConfigPaths({\n projects: ['./tsconfig.json'],\n }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n})\n\nexport default config\n"
|
|
28
|
+
"vite.config.ts": "import { defineConfig } from 'vite'\nimport { devtools } from '@tanstack/devtools-vite'\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite'\nimport viteReact from '@vitejs/plugin-react'\nimport viteTsConfigPaths from 'vite-tsconfig-paths'\nimport { fileURLToPath, URL } from 'url'\nimport tailwindcss from '@tailwindcss/vite'\n\nconst config = defineConfig({\n resolve: {\n alias: {\n '@': fileURLToPath(new URL('./src', import.meta.url)),\n },\n },\n plugins: [\n devtools(),\n // this is the plugin that enables path aliases\n viteTsConfigPaths({\n projects: ['./tsconfig.json'],\n }),\n tailwindcss(),\n tanstackStart(),\n viteReact(),\n ],\n})\n\nexport default config\n"
|
|
29
29
|
},
|
|
30
30
|
"commands": [
|
|
31
31
|
"git init",
|