@tanstack/create 0.61.5 → 0.62.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 (147) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/config-file.js +5 -2
  3. package/dist/custom-add-ons/starter.js +45 -28
  4. package/dist/file-helpers.js +1 -0
  5. package/dist/frameworks/react/add-ons/shadcn/assets/src/styles.css +224 -15
  6. package/dist/frameworks/react/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  7. package/dist/frameworks/react/add-ons/store/assets/src/routes/demo/store.tsx.ejs +1 -1
  8. package/dist/frameworks/react/add-ons/store/package.json +2 -2
  9. package/dist/frameworks/react/add-ons/strapi/README.md +158 -8
  10. package/dist/frameworks/react/add-ons/strapi/assets/_dot_env.local.append +1 -1
  11. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/block-renderer.tsx +55 -0
  12. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/index.ts +14 -0
  13. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/media.tsx +27 -0
  14. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/quote.tsx +19 -0
  15. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/rich-text.tsx +11 -0
  16. package/dist/frameworks/react/add-ons/strapi/assets/src/components/blocks/slider.tsx +28 -0
  17. package/dist/frameworks/react/add-ons/strapi/assets/src/components/markdown-content.tsx +74 -0
  18. package/dist/frameworks/react/add-ons/strapi/assets/src/components/pagination.tsx +120 -0
  19. package/dist/frameworks/react/add-ons/strapi/assets/src/components/search.tsx +35 -0
  20. package/dist/frameworks/react/add-ons/strapi/assets/src/components/strapi-image.tsx +47 -0
  21. package/dist/frameworks/react/add-ons/strapi/assets/src/data/loaders/articles.ts +106 -0
  22. package/dist/frameworks/react/add-ons/strapi/assets/src/data/loaders/index.ts +28 -0
  23. package/dist/frameworks/react/add-ons/strapi/assets/src/data/strapi-sdk.ts +9 -0
  24. package/dist/frameworks/react/add-ons/strapi/assets/src/lib/strapi-utils.ts +25 -0
  25. package/dist/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.$articleId.tsx +170 -0
  26. package/dist/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.tsx +269 -43
  27. package/dist/frameworks/react/add-ons/strapi/assets/src/types/strapi.ts +90 -0
  28. package/dist/frameworks/react/add-ons/strapi/info.json +3 -3
  29. package/dist/frameworks/react/add-ons/strapi/package.json +5 -2
  30. package/dist/frameworks/react/index.js +2 -2
  31. package/dist/frameworks/react/project/base/content/blog/fifth-post.mdx.ejs +54 -0
  32. package/dist/frameworks/react/project/base/content/blog/first-post.md.ejs +47 -0
  33. package/dist/frameworks/react/project/base/content/blog/fourth-post.md.ejs +42 -0
  34. package/dist/frameworks/react/project/base/content/blog/second-post.mdx.ejs +46 -0
  35. package/dist/frameworks/react/project/base/content/blog/third-post.md.ejs +49 -0
  36. package/dist/frameworks/react/project/base/content-collections.ts.ejs +37 -0
  37. package/dist/frameworks/react/project/base/package.json +8 -1
  38. package/dist/frameworks/react/project/base/public/images/lagoon-1.svg +13 -0
  39. package/dist/frameworks/react/project/base/public/images/lagoon-2.svg +12 -0
  40. package/dist/frameworks/react/project/base/public/images/lagoon-3.svg +12 -0
  41. package/dist/frameworks/react/project/base/public/images/lagoon-4.svg +12 -0
  42. package/dist/frameworks/react/project/base/public/images/lagoon-5.svg +12 -0
  43. package/dist/frameworks/react/project/base/public/images/lagoon-about.svg +14 -0
  44. package/dist/frameworks/react/project/base/src/components/Footer.tsx.ejs +42 -0
  45. package/dist/frameworks/react/project/base/src/components/Header.tsx.ejs +92 -138
  46. package/dist/frameworks/react/project/base/src/components/MdxCallout.tsx.ejs +16 -0
  47. package/dist/frameworks/react/project/base/src/components/MdxMetrics.tsx.ejs +23 -0
  48. package/dist/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs +81 -0
  49. package/dist/frameworks/react/project/base/src/lib/site.ts.ejs +4 -0
  50. package/dist/frameworks/react/project/base/src/main.tsx.ejs +0 -1
  51. package/dist/frameworks/react/project/base/src/routes/__root.tsx.ejs +10 -6
  52. package/dist/frameworks/react/project/base/src/routes/about.tsx.ejs +27 -0
  53. package/dist/frameworks/react/project/base/src/routes/blog.$slug.tsx.ejs +71 -0
  54. package/dist/frameworks/react/project/base/src/routes/blog.index.tsx.ejs +93 -0
  55. package/dist/frameworks/react/project/base/src/routes/index.tsx.ejs +58 -91
  56. package/dist/frameworks/react/project/base/src/routes/rss[.]xml.ts.ejs +35 -0
  57. package/dist/frameworks/react/project/base/src/styles.css.ejs +268 -6
  58. package/dist/frameworks/react/project/base/tsconfig.json.ejs +2 -0
  59. package/dist/frameworks/react/project/base/vite.config.ts.ejs +2 -0
  60. package/dist/frameworks/solid/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  61. package/dist/frameworks/solid/add-ons/store/assets/src/routes/demo.store.tsx.ejs +2 -2
  62. package/dist/frameworks/solid/examples/tanchat/assets/src/lib/demo-store.ts +5 -6
  63. package/dist/frameworks/solid/project/base/src/components/Header.tsx.ejs +8 -6
  64. package/dist/frameworks/solid/project/base/src/routes/__root.tsx.ejs +1 -1
  65. package/dist/frameworks/solid/project/base/src/routes/index.tsx.ejs +1 -1
  66. package/dist/frameworks.js +3 -0
  67. package/dist/package-json.js +1 -1
  68. package/dist/registry.js +21 -4
  69. package/dist/types/registry.d.ts +38 -0
  70. package/package.json +1 -1
  71. package/src/config-file.ts +6 -2
  72. package/src/custom-add-ons/starter.ts +30 -10
  73. package/src/file-helpers.ts +1 -0
  74. package/src/frameworks/react/add-ons/shadcn/assets/src/styles.css +224 -15
  75. package/src/frameworks/react/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  76. package/src/frameworks/react/add-ons/store/assets/src/routes/demo/store.tsx.ejs +1 -1
  77. package/src/frameworks/react/add-ons/store/package.json +2 -2
  78. package/src/frameworks/react/add-ons/strapi/README.md +158 -8
  79. package/src/frameworks/react/add-ons/strapi/assets/_dot_env.local.append +1 -1
  80. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/block-renderer.tsx +55 -0
  81. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/index.ts +14 -0
  82. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/media.tsx +27 -0
  83. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/quote.tsx +19 -0
  84. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/rich-text.tsx +11 -0
  85. package/src/frameworks/react/add-ons/strapi/assets/src/components/blocks/slider.tsx +28 -0
  86. package/src/frameworks/react/add-ons/strapi/assets/src/components/markdown-content.tsx +74 -0
  87. package/src/frameworks/react/add-ons/strapi/assets/src/components/pagination.tsx +120 -0
  88. package/src/frameworks/react/add-ons/strapi/assets/src/components/search.tsx +35 -0
  89. package/src/frameworks/react/add-ons/strapi/assets/src/components/strapi-image.tsx +47 -0
  90. package/src/frameworks/react/add-ons/strapi/assets/src/data/loaders/articles.ts +106 -0
  91. package/src/frameworks/react/add-ons/strapi/assets/src/data/loaders/index.ts +28 -0
  92. package/src/frameworks/react/add-ons/strapi/assets/src/data/strapi-sdk.ts +9 -0
  93. package/src/frameworks/react/add-ons/strapi/assets/src/lib/strapi-utils.ts +25 -0
  94. package/src/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.$articleId.tsx +170 -0
  95. package/src/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi.tsx +269 -43
  96. package/src/frameworks/react/add-ons/strapi/assets/src/types/strapi.ts +90 -0
  97. package/src/frameworks/react/add-ons/strapi/info.json +3 -3
  98. package/src/frameworks/react/add-ons/strapi/package.json +5 -2
  99. package/src/frameworks/react/index.ts +2 -2
  100. package/src/frameworks/react/project/base/content/blog/fifth-post.mdx.ejs +54 -0
  101. package/src/frameworks/react/project/base/content/blog/first-post.md.ejs +47 -0
  102. package/src/frameworks/react/project/base/content/blog/fourth-post.md.ejs +42 -0
  103. package/src/frameworks/react/project/base/content/blog/second-post.mdx.ejs +46 -0
  104. package/src/frameworks/react/project/base/content/blog/third-post.md.ejs +49 -0
  105. package/src/frameworks/react/project/base/content-collections.ts.ejs +37 -0
  106. package/src/frameworks/react/project/base/package.json +8 -1
  107. package/src/frameworks/react/project/base/public/images/lagoon-1.svg +13 -0
  108. package/src/frameworks/react/project/base/public/images/lagoon-2.svg +12 -0
  109. package/src/frameworks/react/project/base/public/images/lagoon-3.svg +12 -0
  110. package/src/frameworks/react/project/base/public/images/lagoon-4.svg +12 -0
  111. package/src/frameworks/react/project/base/public/images/lagoon-5.svg +12 -0
  112. package/src/frameworks/react/project/base/public/images/lagoon-about.svg +14 -0
  113. package/src/frameworks/react/project/base/src/components/Footer.tsx.ejs +42 -0
  114. package/src/frameworks/react/project/base/src/components/Header.tsx.ejs +92 -138
  115. package/src/frameworks/react/project/base/src/components/MdxCallout.tsx.ejs +16 -0
  116. package/src/frameworks/react/project/base/src/components/MdxMetrics.tsx.ejs +23 -0
  117. package/src/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs +81 -0
  118. package/src/frameworks/react/project/base/src/lib/site.ts.ejs +4 -0
  119. package/src/frameworks/react/project/base/src/main.tsx.ejs +0 -1
  120. package/src/frameworks/react/project/base/src/routes/__root.tsx.ejs +10 -6
  121. package/src/frameworks/react/project/base/src/routes/about.tsx.ejs +27 -0
  122. package/src/frameworks/react/project/base/src/routes/blog.$slug.tsx.ejs +71 -0
  123. package/src/frameworks/react/project/base/src/routes/blog.index.tsx.ejs +93 -0
  124. package/src/frameworks/react/project/base/src/routes/index.tsx.ejs +58 -91
  125. package/src/frameworks/react/project/base/src/routes/rss[.]xml.ts.ejs +35 -0
  126. package/src/frameworks/react/project/base/src/styles.css.ejs +268 -6
  127. package/src/frameworks/react/project/base/tsconfig.json.ejs +2 -0
  128. package/src/frameworks/react/project/base/vite.config.ts.ejs +2 -0
  129. package/src/frameworks/solid/add-ons/store/assets/src/lib/demo-store.ts +5 -6
  130. package/src/frameworks/solid/add-ons/store/assets/src/routes/demo.store.tsx.ejs +2 -2
  131. package/src/frameworks/solid/examples/tanchat/assets/src/lib/demo-store.ts +5 -6
  132. package/src/frameworks/solid/project/base/src/components/Header.tsx.ejs +8 -6
  133. package/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs +1 -1
  134. package/src/frameworks/solid/project/base/src/routes/index.tsx.ejs +1 -1
  135. package/src/frameworks.ts +4 -0
  136. package/src/package-json.ts +1 -1
  137. package/src/registry.ts +28 -4
  138. package/tests/add-ons.test.ts +4 -4
  139. package/tests/config-file.test.ts +3 -3
  140. package/tests/custom-add-ons/starter.test.ts +34 -2
  141. package/tests/frameworks.test.ts +24 -0
  142. package/tests/options.test.ts +4 -4
  143. package/tests/utils.test.ts +2 -2
  144. package/dist/frameworks/react/add-ons/strapi/assets/src/lib/strapiClient.ts +0 -7
  145. package/dist/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi_.$articleId.tsx +0 -78
  146. package/src/frameworks/react/add-ons/strapi/assets/src/lib/strapiClient.ts +0 -7
  147. package/src/frameworks/react/add-ons/strapi/assets/src/routes/demo/strapi_.$articleId.tsx +0 -78
@@ -1,64 +1,290 @@
1
- import { articles } from '#/lib/strapiClient'
2
1
  import { createFileRoute, Link } from '@tanstack/react-router'
2
+ import { z } from 'zod'
3
+ import { strapiApi } from '@/data/loaders'
4
+ import { StrapiImage } from '@/components/strapi-image'
5
+ import { Search } from '@/components/search'
6
+ import { Pagination } from '@/components/pagination'
7
+ import type { TArticle } from '@/types/strapi'
8
+
9
+ type LoaderResult = {
10
+ status: 'success' | 'empty' | 'error'
11
+ articles: TArticle[]
12
+ meta?: { pagination?: { page: number; pageCount: number; total: number } }
13
+ error?: string
14
+ query?: string
15
+ }
16
+
17
+ const searchSchema = z.object({
18
+ query: z.string().optional(),
19
+ page: z.number().default(1),
20
+ })
3
21
 
4
22
  export const Route = createFileRoute('/demo/strapi')({
5
23
  component: RouteComponent,
6
- loader: async () => {
7
- const { data: strapiArticles } = await articles.find()
8
- return strapiArticles
24
+ validateSearch: searchSchema,
25
+ loaderDeps: ({ search }) => ({ search }),
26
+ loader: async ({ deps }): Promise<LoaderResult> => {
27
+ const { query, page } = deps.search
28
+ try {
29
+ const response = await strapiApi.articles.getArticlesData({
30
+ data: { query, page },
31
+ })
32
+
33
+ // Check if we got data
34
+ if (!response || !response.data) {
35
+ return {
36
+ status: 'empty',
37
+ articles: [],
38
+ meta: response?.meta,
39
+ query,
40
+ }
41
+ }
42
+
43
+ // Check if data array is empty
44
+ if (response.data.length === 0) {
45
+ return {
46
+ status: 'empty',
47
+ articles: [],
48
+ meta: response.meta,
49
+ query,
50
+ }
51
+ }
52
+
53
+ return {
54
+ status: 'success',
55
+ articles: response.data,
56
+ meta: response.meta,
57
+ query,
58
+ }
59
+ } catch (error) {
60
+ console.error('Strapi fetch error:', error)
61
+ return {
62
+ status: 'error',
63
+ articles: [],
64
+ error:
65
+ error instanceof Error
66
+ ? error.message
67
+ : 'Failed to connect to Strapi',
68
+ query,
69
+ }
70
+ }
9
71
  },
10
72
  })
11
73
 
74
+ function StrapiServerInstructions() {
75
+ return (
76
+ <div className="bg-slate-800/50 rounded-lg p-6 text-left mt-6">
77
+ <h2 className="text-lg font-semibold text-white mb-4">
78
+ Start the Strapi Server
79
+ </h2>
80
+ <div className="space-y-2 text-sm font-mono text-gray-400">
81
+ <p>
82
+ <span className="text-cyan-400">$</span> cd ../server
83
+ </p>
84
+ <p>
85
+ <span className="text-cyan-400">$</span> npm install
86
+ </p>
87
+ <p>
88
+ <span className="text-cyan-400">$</span> npm run develop
89
+ </p>
90
+ </div>
91
+ <p className="text-gray-500 text-sm mt-4">
92
+ Then create an admin at{' '}
93
+ <a
94
+ href="http://localhost:1337/admin"
95
+ target="_blank"
96
+ rel="noopener noreferrer"
97
+ className="text-cyan-400 hover:underline"
98
+ >
99
+ http://localhost:1337/admin
100
+ </a>
101
+ </p>
102
+ </div>
103
+ )
104
+ }
105
+
106
+ function ConnectionError({ error }: { error?: string }) {
107
+ return (
108
+ <div className="bg-amber-900/20 border border-amber-500/50 rounded-xl p-8">
109
+ <div className="flex items-start gap-4">
110
+ <div className="text-amber-400 text-2xl">⚠️</div>
111
+ <div>
112
+ <h2 className="text-xl font-semibold text-amber-400 mb-2">
113
+ Cannot Connect to Strapi
114
+ </h2>
115
+ <p className="text-gray-300 mb-4">
116
+ Make sure your Strapi server is running at{' '}
117
+ <code className="text-cyan-400 bg-slate-800 px-2 py-1 rounded">
118
+ http://localhost:1337
119
+ </code>
120
+ </p>
121
+ {error && (
122
+ <p className="text-gray-500 text-sm mb-4">Error: {error}</p>
123
+ )}
124
+ <StrapiServerInstructions />
125
+ </div>
126
+ </div>
127
+ </div>
128
+ )
129
+ }
130
+
131
+ function NoArticlesFound({ query }: { query?: string }) {
132
+ if (query) {
133
+ return (
134
+ <div className="bg-slate-800/50 border border-slate-700 rounded-xl p-8">
135
+ <div className="text-center">
136
+ <div className="text-6xl mb-4">🔍</div>
137
+ <h2 className="text-2xl font-semibold text-white mb-4">
138
+ No Results Found
139
+ </h2>
140
+ <p className="text-gray-400 mb-6 max-w-md mx-auto">
141
+ No articles match your search for "{query}". Try adjusting your
142
+ search terms.
143
+ </p>
144
+ </div>
145
+ </div>
146
+ )
147
+ }
148
+
149
+ return (
150
+ <div className="bg-slate-800/50 border border-slate-700 rounded-xl p-8">
151
+ <div className="text-center">
152
+ <div className="text-6xl mb-4">📝</div>
153
+ <h2 className="text-2xl font-semibold text-white mb-4">
154
+ No Articles Yet
155
+ </h2>
156
+ <p className="text-gray-400 mb-6 max-w-md mx-auto">
157
+ Your Strapi server is running, but there are no published articles.
158
+ Create and publish your first article to see it here.
159
+ </p>
160
+
161
+ <div className="bg-slate-900/50 rounded-lg p-6 text-left max-w-md mx-auto">
162
+ <h3 className="text-lg font-semibold text-white mb-4">
163
+ How to add articles:
164
+ </h3>
165
+ <ol className="space-y-3 text-gray-400">
166
+ <li className="flex gap-3">
167
+ <span className="text-cyan-400 font-bold">1.</span>
168
+ <span>
169
+ Open{' '}
170
+ <a
171
+ href="http://localhost:1337/admin"
172
+ target="_blank"
173
+ rel="noopener noreferrer"
174
+ className="text-cyan-400 hover:underline"
175
+ >
176
+ Strapi Admin Panel
177
+ </a>
178
+ </span>
179
+ </li>
180
+ <li className="flex gap-3">
181
+ <span className="text-cyan-400 font-bold">2.</span>
182
+ <span>
183
+ Go to <strong className="text-white">Content Manager</strong> →{' '}
184
+ <strong className="text-white">Article</strong>
185
+ </span>
186
+ </li>
187
+ <li className="flex gap-3">
188
+ <span className="text-cyan-400 font-bold">3.</span>
189
+ <span>
190
+ Click <strong className="text-white">Create new entry</strong>
191
+ </span>
192
+ </li>
193
+ <li className="flex gap-3">
194
+ <span className="text-cyan-400 font-bold">4.</span>
195
+ <span>
196
+ Fill in the details and click{' '}
197
+ <strong className="text-white">Publish</strong>
198
+ </span>
199
+ </li>
200
+ </ol>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ )
205
+ }
206
+
12
207
  function RouteComponent() {
13
- const strapiArticles = Route.useLoaderData()
208
+ const { status, articles, meta, error, query } = Route.useLoaderData()
14
209
 
15
210
  return (
16
211
  <div className="min-h-screen bg-gradient-to-b from-slate-900 via-slate-800 to-slate-900 p-8">
17
212
  <div className="max-w-7xl mx-auto">
18
- <h1 className="text-4xl font-bold mb-8 text-white">
213
+ <h1 className="text-4xl font-bold mb-6 text-white">
19
214
  <span className="bg-gradient-to-r from-cyan-400 to-blue-400 bg-clip-text text-transparent">
20
215
  Strapi
21
216
  </span>{' '}
22
217
  <span className="text-gray-300">Articles</span>
23
218
  </h1>
24
219
 
25
- {strapiArticles && strapiArticles.length > 0 ? (
26
- <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
27
- {strapiArticles.map((article) => (
28
- <Link
29
- key={article.id}
30
- to="/demo/strapi/$articleId"
31
- params={{ articleId: article.documentId }}
32
- className="block"
33
- >
34
- <article className="bg-slate-800/50 backdrop-blur-sm border border-slate-700 rounded-xl p-6 hover:border-cyan-500/50 transition-all duration-300 hover:shadow-lg hover:shadow-cyan-500/10 cursor-pointer h-full">
35
- <h2 className="text-xl font-semibold text-white mb-3">
36
- {article.title || 'Untitled'}
37
- </h2>
38
-
39
- {article.description && (
40
- <p className="text-gray-400 mb-4 leading-relaxed">
41
- {article.description}
42
- </p>
43
- )}
44
-
45
- {article.content && (
46
- <p className="text-gray-400 line-clamp-3 leading-relaxed">
47
- {article.content}
48
- </p>
49
- )}
50
-
51
- {article.createdAt && (
52
- <p className="text-sm text-cyan-400/70 mt-4">
53
- {new Date(article.createdAt).toLocaleDateString()}
54
- </p>
55
- )}
56
- </article>
57
- </Link>
58
- ))}
59
- </div>
60
- ) : (
61
- <p className="text-gray-400">No articles found.</p>
220
+ <div className="mb-8">
221
+ <Search />
222
+ </div>
223
+
224
+ {status === 'error' && <ConnectionError error={error} />}
225
+
226
+ {status === 'empty' && <NoArticlesFound query={query} />}
227
+
228
+ {status === 'success' && (
229
+ <>
230
+ <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
231
+ {articles.map((article: TArticle) => (
232
+ <Link
233
+ key={article.id}
234
+ to="/demo/strapi/$articleId"
235
+ params={{ articleId: article.documentId }}
236
+ className="block"
237
+ >
238
+ <article className="bg-slate-800/50 backdrop-blur-sm border border-slate-700 rounded-xl overflow-hidden hover:border-cyan-500/50 transition-all duration-300 hover:shadow-lg hover:shadow-cyan-500/10 cursor-pointer h-full flex flex-col">
239
+ <StrapiImage
240
+ src={article.cover?.url}
241
+ alt={article.cover?.alternativeText || article.title}
242
+ className="w-full h-48"
243
+ />
244
+
245
+ <div className="p-6 flex flex-col flex-1">
246
+ <h2 className="text-xl font-semibold text-white mb-3">
247
+ {article.title || 'Untitled'}
248
+ </h2>
249
+
250
+ {article.description && (
251
+ <p className="text-gray-400 mb-4 leading-relaxed line-clamp-2">
252
+ {article.description}
253
+ </p>
254
+ )}
255
+
256
+ <div className="mt-auto flex items-center justify-between">
257
+ {article.author?.name && (
258
+ <span className="text-sm text-gray-500">
259
+ By {article.author.name}
260
+ </span>
261
+ )}
262
+ {article.createdAt && (
263
+ <span className="text-sm text-cyan-400/70">
264
+ {new Date(article.createdAt).toLocaleDateString()}
265
+ </span>
266
+ )}
267
+ </div>
268
+
269
+ {article.category?.name && (
270
+ <div className="mt-3">
271
+ <span className="text-xs px-2 py-1 bg-slate-700 text-cyan-400 rounded-full">
272
+ {article.category.name}
273
+ </span>
274
+ </div>
275
+ )}
276
+ </div>
277
+ </article>
278
+ </Link>
279
+ ))}
280
+ </div>
281
+
282
+ {meta?.pagination && meta.pagination.pageCount > 1 && (
283
+ <div className="mt-8">
284
+ <Pagination pageCount={meta.pagination.pageCount} />
285
+ </div>
286
+ )}
287
+ </>
62
288
  )}
63
289
  </div>
64
290
  </div>
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Strapi type definitions
3
+ * These types match the Strapi Cloud Template Blog schema
4
+ */
5
+
6
+ import type { Block } from "@/components/blocks";
7
+
8
+ // Base image type from Strapi media library
9
+ export type TImage = {
10
+ id: number;
11
+ documentId: string;
12
+ alternativeText: string | null;
13
+ url: string;
14
+ };
15
+
16
+ // Author content type
17
+ export type TAuthor = {
18
+ id: number;
19
+ documentId: string;
20
+ name: string;
21
+ email?: string;
22
+ createdAt: string;
23
+ updatedAt: string;
24
+ publishedAt: string;
25
+ };
26
+
27
+ // Category content type
28
+ export type TCategory = {
29
+ id: number;
30
+ documentId: string;
31
+ name: string;
32
+ slug: string;
33
+ description?: string;
34
+ createdAt: string;
35
+ updatedAt: string;
36
+ publishedAt: string;
37
+ };
38
+
39
+ // Article content type
40
+ export type TArticle = {
41
+ id: number;
42
+ documentId: string;
43
+ title: string;
44
+ description: string;
45
+ slug: string;
46
+ cover?: TImage;
47
+ author?: TAuthor;
48
+ category?: TCategory;
49
+ blocks?: Array<Block>;
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ publishedAt: string;
53
+ };
54
+
55
+ // Strapi response wrappers
56
+ export type TStrapiResponseSingle<T> = {
57
+ data: T;
58
+ meta?: {
59
+ pagination?: TStrapiPagination;
60
+ };
61
+ };
62
+
63
+ export type TStrapiResponseCollection<T> = {
64
+ data: Array<T>;
65
+ meta?: {
66
+ pagination?: TStrapiPagination;
67
+ };
68
+ };
69
+
70
+ export type TStrapiPagination = {
71
+ page: number;
72
+ pageSize: number;
73
+ pageCount: number;
74
+ total: number;
75
+ };
76
+
77
+ export type TStrapiError = {
78
+ status: number;
79
+ name: string;
80
+ message: string;
81
+ details?: Record<string, Array<string>>;
82
+ };
83
+
84
+ export type TStrapiResponse<T = null> = {
85
+ data?: T;
86
+ error?: TStrapiError;
87
+ meta?: {
88
+ pagination?: TStrapiPagination;
89
+ };
90
+ };
@@ -11,9 +11,9 @@
11
11
  "routes": [
12
12
  {
13
13
  "url": "/demo/strapi",
14
- "name": "Strapi",
15
- "path": "src/routes/demo.strapi.tsx",
16
- "jsName": "StrapiDemo"
14
+ "name": "Strapi Articles",
15
+ "path": "src/routes/demo/strapi.tsx",
16
+ "jsName": "StrapiArticles"
17
17
  }
18
18
  ]
19
19
  }
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "dependencies": {
3
- "@strapi/client": "^1.5.0"
3
+ "@strapi/client": "^1.6.0",
4
+ "react-markdown": "^9.0.1",
5
+ "remark-gfm": "^4.0.0",
6
+ "use-debounce": "^10.0.0"
4
7
  }
5
- }
8
+ }
@@ -25,9 +25,9 @@ export function createFrameworkDefinition(): FrameworkDefinition {
25
25
  )
26
26
 
27
27
  return {
28
- id: 'react-cra',
28
+ id: 'react',
29
29
  name: 'React',
30
- description: 'Templates for React CRA',
30
+ description: 'Templates for React',
31
31
  version: '0.1.0',
32
32
  base: files,
33
33
  addOns,
@@ -0,0 +1,54 @@
1
+ ---
2
+ title: 'Velvet Runtime Notes'
3
+ description: 'Use color tokens to keep light and dark themes aligned.'
4
+ pubDate: 'Aug 05 2024'
5
+ heroImage: '/images/lagoon-5.svg'
6
+ ---
7
+
8
+ Semantic tokens keep your UI stable while the brand evolves.
9
+
10
+ Instead of hard-coding one-off colors in components, shape a small set of
11
+ variables and map all surfaces to those variables.
12
+
13
+ ## Why this matters
14
+
15
+ - You can restyle the app in minutes instead of days
16
+ - Light and dark themes stay behaviorally consistent
17
+ - Add-on pages inherit your visual identity by default
18
+
19
+ <MdxCallout title="MDX Component Demo">
20
+ This callout is rendered from JSX inside an MDX post. It is useful for
21
+ release notes, warnings, and migration tips where you want stronger visual
22
+ emphasis than plain markdown blocks.
23
+ </MdxCallout>
24
+
25
+ <MdxMetrics
26
+ items={[
27
+ { label: 'Token count', value: '12 core vars' },
28
+ { label: 'Theme modes', value: 'light + dark + auto' },
29
+ { label: 'Restyle time', value: '< 30 min' },
30
+ ]}
31
+ />
32
+
33
+ ### Token-to-component mapping
34
+
35
+ | Token | Typical usage |
36
+ | --- | --- |
37
+ | `--surface` | Card backgrounds |
38
+ | `--line` | Borders and separators |
39
+ | `--lagoon-deep` | Links and active nav |
40
+
41
+ Keep the mapping documented and your team will make fewer ad-hoc styling calls.
42
+
43
+ ### Example: deriving UI from semantic tokens
44
+
45
+ ```tsx
46
+ const button = {
47
+ color: 'var(--sea-ink)',
48
+ background: 'var(--surface)',
49
+ borderColor: 'var(--line)',
50
+ }
51
+ ```
52
+
53
+ MDX is useful here because you can interleave narrative, tables, code blocks,
54
+ and custom JSX components in one authoring surface.
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: 'Neon Mango Protocol'
3
+ description: 'A quick walkthrough of the starter foundations.'
4
+ pubDate: 'Jul 08 2024'
5
+ heroImage: '/images/lagoon-3.svg'
6
+ ---
7
+
8
+ This starter ships with routes, SSR, and a calm visual system out of the box.
9
+
10
+ Start by editing route files, then layer in add-ons as needed.
11
+
12
+ ## What you get on day one
13
+
14
+ - Full-document SSR using TanStack Start
15
+ - Type-safe file routing with generated route types
16
+ - A reusable design token system for light and dark themes
17
+
18
+ The goal is simple: let teams ship product pages and APIs without spending the
19
+ first week wiring framework internals.
20
+
21
+ ### Suggested order of operations
22
+
23
+ 1. Make the home route feel like your product
24
+ 2. Add one feature route and one API route
25
+ 3. Introduce add-ons only after your core UX is clear
26
+
27
+ > Keep the first commit boring. Reliable defaults beat clever setup code.
28
+
29
+ ## Baseline delivery checklist
30
+
31
+ Before introducing custom infra, confirm these are green:
32
+
33
+ - `pnpm dev` starts cleanly
34
+ - one server route returns typed data
35
+ - one API route validates input/output
36
+ - one integration test exercises a full page render
37
+
38
+ When these are in place, you can iterate quickly without losing confidence.
39
+
40
+ ### Example request flow
41
+
42
+ 1. Client navigation enters route loader
43
+ 2. Loader calls server function
44
+ 3. Server function reads data source and returns typed payload
45
+ 4. Route component renders immediately with stable shape
46
+
47
+ That flow is simple, predictable, and easy to debug.
@@ -0,0 +1,42 @@
1
+ ---
2
+ title: 'Static Tide Almanac'
3
+ description: 'Dial in layout polish and image rhythm across cards.'
4
+ pubDate: 'Jul 29 2024'
5
+ heroImage: '/images/lagoon-1.svg'
6
+ ---
7
+
8
+ As your app grows, visual rhythm matters as much as feature scope.
9
+
10
+ Use larger feature cards to call attention to primary content, then support with
11
+ smaller cards for secondary updates.
12
+
13
+ ## Practical layout pattern
14
+
15
+ Use one featured card followed by standard cards in a responsive grid:
16
+
17
+ - `lg:col-span-2` for the featured story
18
+ - regular span for supporting posts
19
+ - consistent card media height below `lg`
20
+
21
+ That gives you hierarchy without reinventing every breakpoint.
22
+
23
+ ### A quick spacing rule
24
+
25
+ Pair spacing in steps of 4 (`p-4`, `p-8`, `gap-4`, `gap-8`) and only break that
26
+ rule for hero sections.
27
+
28
+ ## Card hierarchy recipe
29
+
30
+ For content-heavy indexes, this sequence works well:
31
+
32
+ 1. One featured card with expanded width
33
+ 2. Three to six standard cards for breadth
34
+ 3. Optional utility card for onboarding links
35
+
36
+ Keep title sizes mostly consistent and let width + image treatment carry
37
+ hierarchy. That avoids jarring jumps as breakpoints shift.
38
+
39
+ ### Avoiding layout drift
40
+
41
+ If cards start to look uneven, check image heights first, then paragraph length.
42
+ Consistency there usually fixes 80% of visual noise.
@@ -0,0 +1,46 @@
1
+ ---
2
+ title: 'Paper Lantern Cache'
3
+ description: 'How to shape navigation and page structure.'
4
+ pubDate: 'Jul 15 2024'
5
+ heroImage: '/images/lagoon-4.svg'
6
+ ---
7
+
8
+ Use file-based routes in `src/routes` to grow the app.
9
+
10
+ Keep shared UI in `src/components` and tune visual tokens in `src/styles.css`.
11
+
12
+ ## Route design tips
13
+
14
+ Treat routes like product domains, not technical buckets.
15
+
16
+ - `routes/settings.*` for account surfaces
17
+ - `routes/billing.*` for payment and plan logic
18
+ - `routes/api.*` for server handlers that belong to the same domain
19
+
20
+ When route trees map to business intent, onboarding gets faster and refactors
21
+ hurt less.
22
+
23
+ ```tsx
24
+ // src/routes/billing.index.tsx
25
+ export const Route = createFileRoute('/billing/')({
26
+ component: BillingPage,
27
+ })
28
+ ```
29
+
30
+ <MdxMetrics
31
+ items={[
32
+ { label: 'Route setup', value: '~10 min' },
33
+ { label: 'Type safety', value: 'end-to-end' },
34
+ { label: 'Refactor risk', value: 'lowered' },
35
+ ]}
36
+ />
37
+
38
+ ### Collaboration pattern
39
+
40
+ Use this lightweight ownership split:
41
+
42
+ 1. Product owns route naming and URL intent
43
+ 2. Design owns shared layout primitives and tokens
44
+ 3. Engineering owns loaders, actions, and caching
45
+
46
+ This pattern keeps URL design, data loading, and UI composition in one place.