kaddidlehopper 0.5.0 → 0.6.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 (296) hide show
  1. package/dist/cli.js +104 -208
  2. package/dist/index.js +1 -31
  3. package/dist/types/cli.d.ts +1 -8
  4. package/dist/types/types.d.ts +7 -12
  5. package/package.json +1 -2
  6. package/src/cli.ts +129 -271
  7. package/src/index.ts +1 -47
  8. package/src/types.ts +7 -13
  9. package/CONTEXT.md +0 -139
  10. package/add-ons/ai/README.md +0 -34
  11. package/add-ons/ai/assets/AGENTS.md.append +0 -24
  12. package/add-ons/ai/assets/_dot_env.local.append +0 -13
  13. package/add-ons/ai/assets/src/components/AIAssistant.tsx +0 -149
  14. package/add-ons/ai/assets/src/lib/ai-hook.ts +0 -21
  15. package/add-ons/ai/assets/src/lib/weather-tools.ts +0 -30
  16. package/add-ons/ai/assets/src/routes/api.chat.ts +0 -94
  17. package/add-ons/ai/assets/src/routes/chat.css +0 -175
  18. package/add-ons/ai/assets/src/routes/chat.tsx +0 -141
  19. package/add-ons/ai/info.json +0 -22
  20. package/add-ons/ai/package.json +0 -17
  21. package/add-ons/ai/small-logo.svg +0 -8
  22. package/add-ons/db/assets/AGENTS.md.append +0 -22
  23. package/add-ons/db/assets/DB-SETUP.md +0 -65
  24. package/add-ons/db/assets/_dot_env.local.append +0 -2
  25. package/add-ons/db/assets/drizzle.config.ts +0 -10
  26. package/add-ons/db/assets/src/db/index.ts +0 -8
  27. package/add-ons/db/assets/src/db/schema.ts +0 -8
  28. package/add-ons/db/assets/src/routes/db-example.tsx +0 -118
  29. package/add-ons/db/assets/src/server/guestbook.functions.ts +0 -23
  30. package/add-ons/db/info.json +0 -22
  31. package/add-ons/db/package.json +0 -10
  32. package/add-ons/forms/assets/AGENTS.md.append +0 -13
  33. package/add-ons/forms/assets/public/form-example.html +0 -14
  34. package/add-ons/forms/assets/src/routes/form-example.tsx +0 -76
  35. package/add-ons/forms/info.json +0 -18
  36. package/add-ons/forms/package.json +0 -3
  37. package/examples/ai-chat/README.md +0 -36
  38. package/examples/ai-chat/assets/AGENTS.md.append +0 -24
  39. package/examples/ai-chat/assets/src/lib/ai-hook.ts +0 -21
  40. package/examples/ai-chat/assets/src/lib/weather-tools.ts +0 -30
  41. package/examples/ai-chat/assets/src/routes/__root.tsx +0 -57
  42. package/examples/ai-chat/assets/src/routes/api.chat.ts +0 -94
  43. package/examples/ai-chat/assets/src/routes/index.tsx +0 -141
  44. package/examples/ai-chat/assets/src/styles.css +0 -165
  45. package/examples/ai-chat/info.json +0 -25
  46. package/examples/ai-chat/package.json +0 -14
  47. package/examples/blog/README.md +0 -60
  48. package/examples/blog/assets/AGENTS.md.append +0 -18
  49. package/examples/blog/assets/content/posts/beach.md +0 -12
  50. package/examples/blog/assets/content/posts/jungle.md.ejs +0 -12
  51. package/examples/blog/assets/content/posts/mountains.md.ejs +0 -12
  52. package/examples/blog/assets/content/posts/snorkeling.md.ejs +0 -12
  53. package/examples/blog/assets/content/posts/waterfall.md.ejs +0 -12
  54. package/examples/blog/assets/content-collections.ts +0 -30
  55. package/examples/blog/assets/public/beach.jpg +0 -0
  56. package/examples/blog/assets/public/jungle.jpg +0 -0
  57. package/examples/blog/assets/public/mountains.jpg +0 -0
  58. package/examples/blog/assets/public/snorkeling.jpg +0 -0
  59. package/examples/blog/assets/public/waterfall.jpg +0 -0
  60. package/examples/blog/assets/src/components/Header.tsx +0 -52
  61. package/examples/blog/assets/src/components/VacayAssistant.tsx +0 -205
  62. package/examples/blog/assets/src/components/blog-posts.tsx +0 -78
  63. package/examples/blog/assets/src/components/ui/card.tsx +0 -92
  64. package/examples/blog/assets/src/lib/blog-ai-hook.ts +0 -25
  65. package/examples/blog/assets/src/lib/blog-tools.ts +0 -111
  66. package/examples/blog/assets/src/lib/utils.ts +0 -6
  67. package/examples/blog/assets/src/routes/__root.tsx +0 -57
  68. package/examples/blog/assets/src/routes/api.blog-chat.ts +0 -117
  69. package/examples/blog/assets/src/routes/category.$category.tsx +0 -19
  70. package/examples/blog/assets/src/routes/index.tsx +0 -19
  71. package/examples/blog/assets/src/routes/posts.$slug.tsx +0 -63
  72. package/examples/blog/assets/src/styles.css +0 -138
  73. package/examples/blog/info.json +0 -40
  74. package/examples/blog/package.json +0 -23
  75. package/examples/calculator/README.md +0 -16
  76. package/examples/calculator/assets/AGENTS.md.append +0 -9
  77. package/examples/calculator/assets/src/components/Calculator.tsx +0 -238
  78. package/examples/calculator/assets/src/components/Header.tsx +0 -17
  79. package/examples/calculator/assets/src/routes/__root.tsx +0 -57
  80. package/examples/calculator/assets/src/routes/index.tsx +0 -14
  81. package/examples/calculator/info.json +0 -20
  82. package/examples/calculator/package.json +0 -1
  83. package/examples/dashboard/README.md +0 -17
  84. package/examples/dashboard/assets/AGENTS.md.append +0 -12
  85. package/examples/dashboard/assets/src/components/Header.tsx +0 -17
  86. package/examples/dashboard/assets/src/routes/__root.tsx +0 -57
  87. package/examples/dashboard/assets/src/routes/index.tsx +0 -158
  88. package/examples/dashboard/info.json +0 -19
  89. package/examples/dashboard/package.json +0 -6
  90. package/examples/ecommerce/README.md +0 -48
  91. package/examples/ecommerce/assets/AGENTS.md.append +0 -22
  92. package/examples/ecommerce/assets/public/logo.png +0 -0
  93. package/examples/ecommerce/assets/public/motorcycle-scooter.jpg +0 -0
  94. package/examples/ecommerce/assets/src/components/BuyButton.tsx +0 -35
  95. package/examples/ecommerce/assets/src/components/Header.tsx +0 -36
  96. package/examples/ecommerce/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  97. package/examples/ecommerce/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  98. package/examples/ecommerce/assets/src/data/motorcycles.ts +0 -27
  99. package/examples/ecommerce/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  100. package/examples/ecommerce/assets/src/lib/motorcycle-tools.ts +0 -42
  101. package/examples/ecommerce/assets/src/lib/stripe.server.ts +0 -39
  102. package/examples/ecommerce/assets/src/routes/__root.tsx +0 -57
  103. package/examples/ecommerce/assets/src/routes/api.motorcycle-chat.ts +0 -78
  104. package/examples/ecommerce/assets/src/routes/checkout/cancel.tsx +0 -25
  105. package/examples/ecommerce/assets/src/routes/checkout/success.tsx +0 -25
  106. package/examples/ecommerce/assets/src/routes/index.tsx +0 -76
  107. package/examples/ecommerce/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -55
  108. package/examples/ecommerce/assets/src/store/motorcycle-assistant.ts +0 -3
  109. package/examples/ecommerce/assets/src/styles.css +0 -212
  110. package/examples/ecommerce/info.json +0 -38
  111. package/examples/ecommerce/package.json +0 -13
  112. package/examples/events/README.md +0 -110
  113. package/examples/events/assets/AGENTS.md.append +0 -21
  114. package/examples/events/assets/content/speakers/andre-costa.md +0 -22
  115. package/examples/events/assets/content/speakers/hans-mueller.md.ejs +0 -22
  116. package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +0 -22
  117. package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +0 -22
  118. package/examples/events/assets/content/speakers/marie-dubois.md.ejs +0 -20
  119. package/examples/events/assets/content/speakers/priya-sharma.md.ejs +0 -22
  120. package/examples/events/assets/content/talks/croissant-lamination-secrets.md +0 -39
  121. package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +0 -39
  122. package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +0 -39
  123. package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +0 -39
  124. package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +0 -36
  125. package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +0 -32
  126. package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +0 -39
  127. package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +0 -39
  128. package/examples/events/assets/content-collections.ts +0 -56
  129. package/examples/events/assets/public/background-1.jpg +0 -0
  130. package/examples/events/assets/public/background-2.jpg +0 -0
  131. package/examples/events/assets/public/background-3.jpg +0 -0
  132. package/examples/events/assets/public/background-4.jpg +0 -0
  133. package/examples/events/assets/public/conference-logo.png +0 -0
  134. package/examples/events/assets/public/favicon.ico +0 -0
  135. package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
  136. package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
  137. package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
  138. package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
  139. package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
  140. package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
  141. package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
  142. package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
  143. package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
  144. package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
  145. package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
  146. package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
  147. package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
  148. package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
  149. package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
  150. package/examples/events/assets/public/tanstack-word-logo-white.svg +0 -1
  151. package/examples/events/assets/src/components/Header.tsx +0 -59
  152. package/examples/events/assets/src/components/HeaderNav.tsx +0 -67
  153. package/examples/events/assets/src/components/HeroCarousel.tsx +0 -61
  154. package/examples/events/assets/src/components/RemyAssistant.tsx +0 -207
  155. package/examples/events/assets/src/components/SpeakerCard.tsx +0 -67
  156. package/examples/events/assets/src/components/TalkCard.tsx +0 -77
  157. package/examples/events/assets/src/components/ui/card.tsx +0 -92
  158. package/examples/events/assets/src/lib/conference-ai-hook.ts +0 -26
  159. package/examples/events/assets/src/lib/conference-tools.ts +0 -210
  160. package/examples/events/assets/src/lib/model-selection.ts +0 -1
  161. package/examples/events/assets/src/lib/utils.ts +0 -6
  162. package/examples/events/assets/src/routes/__root.tsx +0 -70
  163. package/examples/events/assets/src/routes/api.remy-chat.ts +0 -119
  164. package/examples/events/assets/src/routes/index.tsx +0 -192
  165. package/examples/events/assets/src/routes/schedule.index.tsx +0 -274
  166. package/examples/events/assets/src/routes/speakers.$slug.tsx +0 -122
  167. package/examples/events/assets/src/routes/speakers.index.tsx +0 -40
  168. package/examples/events/assets/src/routes/talks.$slug.tsx +0 -116
  169. package/examples/events/assets/src/routes/talks.index.tsx +0 -40
  170. package/examples/events/assets/src/styles.css +0 -182
  171. package/examples/events/info.json +0 -61
  172. package/examples/events/package.json +0 -23
  173. package/examples/marketing/README.md +0 -60
  174. package/examples/marketing/assets/AGENTS.md.append +0 -15
  175. package/examples/marketing/assets/public/logo.png +0 -0
  176. package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
  177. package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
  178. package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
  179. package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
  180. package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
  181. package/examples/marketing/assets/src/components/Header.tsx +0 -36
  182. package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  183. package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  184. package/examples/marketing/assets/src/data/motorcycles.ts.ejs +0 -77
  185. package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  186. package/examples/marketing/assets/src/lib/motorcycle-tools.ts +0 -42
  187. package/examples/marketing/assets/src/routes/__root.tsx +0 -57
  188. package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +0 -78
  189. package/examples/marketing/assets/src/routes/index.tsx +0 -72
  190. package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -56
  191. package/examples/marketing/assets/src/store/motorcycle-assistant.ts +0 -3
  192. package/examples/marketing/assets/src/styles.css +0 -212
  193. package/examples/marketing/info.json +0 -33
  194. package/examples/marketing/package.json +0 -14
  195. package/examples/portfolio/README.md +0 -49
  196. package/examples/portfolio/assets/AGENTS.md.append +0 -21
  197. package/examples/portfolio/assets/content/blog/getting-started-with-tanstack.md +0 -53
  198. package/examples/portfolio/assets/content/blog/react-19-features.md +0 -78
  199. package/examples/portfolio/assets/content/blog/tailwind-css-v4-guide.md +0 -60
  200. package/examples/portfolio/assets/content/education/code-school.md +0 -17
  201. package/examples/portfolio/assets/content/jobs/initech-junior.md +0 -20
  202. package/examples/portfolio/assets/content/projects/portfolio-site.md +0 -15
  203. package/examples/portfolio/assets/content/projects/task-manager.md +0 -15
  204. package/examples/portfolio/assets/content-collections.ts +0 -65
  205. package/examples/portfolio/assets/public/contact.html +0 -6
  206. package/examples/portfolio/assets/public/headshot-on-white.jpg +0 -0
  207. package/examples/portfolio/assets/src/components/Header.tsx +0 -33
  208. package/examples/portfolio/assets/src/components/ResumeAssistant.tsx +0 -193
  209. package/examples/portfolio/assets/src/components/ui/badge.tsx +0 -46
  210. package/examples/portfolio/assets/src/components/ui/card.tsx +0 -92
  211. package/examples/portfolio/assets/src/components/ui/checkbox.tsx +0 -30
  212. package/examples/portfolio/assets/src/components/ui/hover-card.tsx +0 -44
  213. package/examples/portfolio/assets/src/components/ui/separator.tsx +0 -26
  214. package/examples/portfolio/assets/src/lib/resume-ai-hook.ts +0 -21
  215. package/examples/portfolio/assets/src/lib/resume-tools.ts +0 -165
  216. package/examples/portfolio/assets/src/lib/utils.ts +0 -6
  217. package/examples/portfolio/assets/src/routes/__root.tsx +0 -57
  218. package/examples/portfolio/assets/src/routes/api.resume-chat.ts +0 -116
  219. package/examples/portfolio/assets/src/routes/blog/$slug.tsx +0 -73
  220. package/examples/portfolio/assets/src/routes/contact.tsx +0 -121
  221. package/examples/portfolio/assets/src/routes/index.tsx +0 -55
  222. package/examples/portfolio/assets/src/routes/projects.tsx +0 -62
  223. package/examples/portfolio/assets/src/routes/resume.tsx +0 -220
  224. package/examples/portfolio/assets/src/styles.css +0 -138
  225. package/examples/portfolio/info.json +0 -50
  226. package/examples/portfolio/package.json +0 -26
  227. package/examples/resume/README.md +0 -82
  228. package/examples/resume/assets/AGENTS.md.append +0 -19
  229. package/examples/resume/assets/content/education/code-school.md +0 -17
  230. package/examples/resume/assets/content/jobs/freelance.md.ejs +0 -13
  231. package/examples/resume/assets/content/jobs/initech-junior.md +0 -20
  232. package/examples/resume/assets/content/jobs/initech-lead.md.ejs +0 -29
  233. package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +0 -28
  234. package/examples/resume/assets/content-collections.ts +0 -36
  235. package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
  236. package/examples/resume/assets/src/components/Header.tsx +0 -33
  237. package/examples/resume/assets/src/components/ResumeAssistant.tsx +0 -193
  238. package/examples/resume/assets/src/components/ui/badge.tsx +0 -46
  239. package/examples/resume/assets/src/components/ui/card.tsx +0 -92
  240. package/examples/resume/assets/src/components/ui/checkbox.tsx +0 -30
  241. package/examples/resume/assets/src/components/ui/hover-card.tsx +0 -44
  242. package/examples/resume/assets/src/components/ui/separator.tsx +0 -26
  243. package/examples/resume/assets/src/lib/resume-ai-hook.ts +0 -21
  244. package/examples/resume/assets/src/lib/resume-tools.ts +0 -165
  245. package/examples/resume/assets/src/lib/utils.ts +0 -6
  246. package/examples/resume/assets/src/routes/api.resume-chat.ts +0 -110
  247. package/examples/resume/assets/src/routes/index.tsx +0 -220
  248. package/examples/resume/assets/src/styles.css +0 -138
  249. package/examples/resume/info.json +0 -35
  250. package/examples/resume/package.json +0 -26
  251. package/examples/saas/README.md +0 -16
  252. package/examples/saas/assets/AGENTS.md.append +0 -9
  253. package/examples/saas/assets/src/components/Header.tsx +0 -17
  254. package/examples/saas/assets/src/routes/__root.tsx +0 -57
  255. package/examples/saas/assets/src/routes/faq.tsx +0 -94
  256. package/examples/saas/assets/src/routes/index.tsx +0 -197
  257. package/examples/saas/info.json +0 -23
  258. package/examples/saas/package.json +0 -1
  259. package/examples/survey/README.md +0 -20
  260. package/examples/survey/assets/AGENTS.md.append +0 -9
  261. package/examples/survey/assets/public/form-survey.html +0 -15
  262. package/examples/survey/assets/src/components/SurveyForm.tsx +0 -128
  263. package/examples/survey/assets/src/routes/index.tsx +0 -14
  264. package/examples/survey/info.json +0 -19
  265. package/examples/survey/package.json +0 -1
  266. package/project/base/AGENTS.md +0 -86
  267. package/project/base/_dot_claude/skills/content-collections/SKILL.md +0 -505
  268. package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +0 -410
  269. package/project/base/_dot_claude/skills/netlify-db/SKILL.md +0 -424
  270. package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +0 -419
  271. package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +0 -243
  272. package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +0 -372
  273. package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +0 -421
  274. package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +0 -426
  275. package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +0 -493
  276. package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +0 -430
  277. package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +0 -445
  278. package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +0 -494
  279. package/project/base/_dot_gitignore +0 -8
  280. package/project/base/netlify.toml +0 -7
  281. package/project/base/package.json +0 -33
  282. package/project/base/public/favicon.ico +0 -0
  283. package/project/base/public/tanstack-circle-logo.png +0 -0
  284. package/project/base/public/tanstack-word-logo-white.svg +0 -1
  285. package/project/base/src/components/Header.tsx +0 -17
  286. package/project/base/src/components/HeaderNav.tsx.ejs +0 -179
  287. package/project/base/src/router.tsx +0 -15
  288. package/project/base/src/routes/__root.tsx +0 -57
  289. package/project/base/src/routes/index.tsx +0 -48
  290. package/project/base/src/styles.css +0 -15
  291. package/project/base/tsconfig.json +0 -28
  292. package/project/base/vite.config.ts.ejs +0 -25
  293. package/project/info.json +0 -17
  294. package/project/packages.json +0 -22
  295. package/scripts/check-outdated-packages.js +0 -421
  296. package/src/agents-md.ts +0 -139
@@ -1,48 +0,0 @@
1
- # Ecommerce Example
2
-
3
- A motorcycle ecommerce site built with TanStack Start, Stripe checkout, and an AI shopping assistant for Netlify deployment.
4
-
5
- ## Features
6
-
7
- - **Product Catalog**: Motorcycle listings with details and images
8
- - **Stripe Checkout**: Secure payment processing via Stripe
9
- - **AI Shopping Assistant**: Chat-based motorcycle recommendation engine
10
- - **Product Detail Pages**: Individual motorcycle pages with full descriptions
11
- - **Checkout Flow**: Success and cancellation pages
12
-
13
- ## Project Structure
14
-
15
- ```
16
- ├── src/
17
- │ ├── components/
18
- │ │ ├── BuyButton.tsx # Stripe checkout trigger
19
- │ │ ├── Header.tsx # Custom header with AI toggle
20
- │ │ ├── MotorcycleAIAssistant.tsx # AI chat panel
21
- │ │ └── MotorcycleRecommendation.tsx
22
- │ ├── data/
23
- │ │ └── motorcycles.ts # Product data
24
- │ ├── lib/
25
- │ │ ├── motorcycle-ai-hook.ts # Chat hook
26
- │ │ ├── motorcycle-tools.ts # AI tools
27
- │ │ └── stripe.server.ts # Stripe server functions
28
- │ ├── store/
29
- │ │ └── motorcycle-assistant.ts # UI state
30
- │ └── routes/
31
- │ ├── index.tsx # Product listing
32
- │ ├── motorcycles/$motorcycleId.tsx
33
- │ ├── checkout/success.tsx
34
- │ ├── checkout/cancel.tsx
35
- │ └── api.motorcycle-chat.ts
36
- ```
37
-
38
- ## Environment Variables
39
-
40
- - `STRIPE_SECRET_KEY` — Stripe secret key for checkout
41
- - `ANTHROPIC_API_KEY` — For AI assistant
42
- - `SITE_URL` — Base URL for checkout redirects (defaults to localhost:3000)
43
-
44
- ## Development
45
-
46
- ```bash
47
- npm run dev
48
- ```
@@ -1,22 +0,0 @@
1
- ### Ecommerce Integration
2
-
3
- Motorcycle ecommerce site with Stripe checkout and AI shopping assistant.
4
-
5
- **Stripe checkout:**
6
- - `createCheckoutSession` server function in `src/lib/stripe.server.ts`
7
- - BuyButton component redirects to Stripe Checkout
8
- - Routes: `/checkout/success`, `/checkout/cancel`
9
-
10
- **AI tools available:**
11
- - `getMotorcycles` - Get all motorcycles from catalog
12
- - `recommendMotorcycle` - Display motorcycle recommendation card (MUST use for recommendations; do not write recommendations manually)
13
-
14
- **Dependencies:** stripe, @tanstack/ai, streamdown
15
-
16
- ## Environment Variables
17
-
18
- ```
19
- STRIPE_SECRET_KEY=... # Required for checkout
20
- ```
21
-
22
- For AI assistant: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, or OLLAMA_BASE_URL (same as ai add-on).
@@ -1,35 +0,0 @@
1
- import { useState } from 'react'
2
- import { createCheckoutSession } from '@/lib/stripe.server'
3
-
4
- export function BuyButton({
5
- motorcycleId,
6
- className = '',
7
- }: {
8
- motorcycleId: number
9
- className?: string
10
- }) {
11
- const [loading, setLoading] = useState(false)
12
-
13
- const handleClick = async () => {
14
- setLoading(true)
15
- try {
16
- const url = await createCheckoutSession({ data: motorcycleId })
17
- if (url) {
18
- window.location.href = url
19
- }
20
- } catch (error) {
21
- console.error('Checkout error:', error)
22
- setLoading(false)
23
- }
24
- }
25
-
26
- return (
27
- <button
28
- onClick={handleClick}
29
- disabled={loading}
30
- className={`bg-emerald-600 hover:bg-emerald-500 disabled:bg-emerald-800 disabled:cursor-wait text-white px-6 py-2 rounded-lg transition-colors ${className}`}
31
- >
32
- {loading ? 'Processing...' : 'Buy Now'}
33
- </button>
34
- )
35
- }
@@ -1,36 +0,0 @@
1
- import { Link } from '@tanstack/react-router'
2
-
3
- import HeaderNav from './HeaderNav'
4
-
5
- import MotorcycleAIAssistant from '@/components/MotorcycleAIAssistant'
6
-
7
- export default function Header() {
8
- return (
9
- <>
10
- <div className="z-40">
11
- <HeaderNav />
12
- </div>
13
- <header className="p-4 flex gap-2 bg-black/30 text-white border-b border-orange-500/20 backdrop-blur-md sticky top-0 z-40">
14
- <nav className="flex flex-row items-center">
15
- <div className="px-4 font-extrabold text-lg">
16
- <Link
17
- to="/"
18
- className="hover:text-orange-400 transition-colors flex flex-row items-center"
19
- >
20
- <img
21
- src="/logo.png"
22
- alt="Luna-C Motorcycles"
23
- className="w-20 h-20"
24
- />
25
- <span className="text-2xl font-bold">Luna-C Motorcycles</span>
26
- </Link>
27
- </div>
28
- </nav>
29
-
30
- <div className="ml-auto">
31
- <MotorcycleAIAssistant />
32
- </div>
33
- </header>
34
- </>
35
- )
36
- }
@@ -1,162 +0,0 @@
1
- import { useEffect, useRef, useState } from 'react'
2
- import { useStore } from '@tanstack/react-store'
3
- import { Send, X } from 'lucide-react'
4
- import { Streamdown } from 'streamdown'
5
-
6
- import { useMotorcycleChat } from '@/lib/motorcycle-ai-hook'
7
- import type { ChatMessages } from '@/lib/ai-hook'
8
- import { showMotorcycleAIAssistant } from '@/store/motorcycle-assistant'
9
-
10
- import MotorcycleRecommendation from './MotorcycleRecommendation'
11
-
12
- function Messages({ messages }: { messages: ChatMessages }) {
13
- const messagesContainerRef = useRef<HTMLDivElement>(null)
14
-
15
- useEffect(() => {
16
- if (messagesContainerRef.current) {
17
- messagesContainerRef.current.scrollTop =
18
- messagesContainerRef.current.scrollHeight
19
- }
20
- }, [messages])
21
-
22
- if (!messages.length) {
23
- return (
24
- <div className="flex-1 flex items-center justify-center text-gray-400 text-sm">
25
- Ask me about your next motorcycle.
26
- </div>
27
- )
28
- }
29
-
30
- return (
31
- <div ref={messagesContainerRef} className="flex-1 overflow-y-auto">
32
- {messages.map(({ id, role, parts }) => (
33
- <div
34
- key={id}
35
- className={`py-3 ${
36
- role === 'assistant'
37
- ? 'bg-linear-to-r from-orange-500/5 to-red-600/5'
38
- : 'bg-transparent'
39
- }`}
40
- >
41
- {parts.map((part, index) => {
42
- if (part.type === 'text' && part.content) {
43
- return (
44
- <div key={index} className="flex items-start gap-2 px-4">
45
- {role === 'assistant' ? (
46
- <div className="w-6 h-6 rounded-lg bg-linear-to-r from-orange-500 to-red-600 flex items-center justify-center text-xs font-medium text-white flex-shrink-0">
47
- AI
48
- </div>
49
- ) : (
50
- <div className="w-6 h-6 rounded-lg bg-gray-700 flex items-center justify-center text-xs font-medium text-white flex-shrink-0">
51
- Y
52
- </div>
53
- )}
54
- <div className="flex-1 min-w-0 text-white prose dark:prose-invert max-w-none prose-sm">
55
- <Streamdown>{part.content}</Streamdown>
56
- </div>
57
- </div>
58
- )
59
- }
60
- if (
61
- part.type === 'tool-call' &&
62
- part.name === 'recommendMotorcycle' &&
63
- part.output
64
- ) {
65
- return (
66
- <div key={part.id} className="max-w-[80%] mx-auto">
67
- <MotorcycleRecommendation id={String(part.output?.id)} />
68
- </div>
69
- )
70
- }
71
- })}
72
- </div>
73
- ))}
74
- </div>
75
- )
76
- }
77
-
78
- export default function AIAssistant() {
79
- const isOpen = useStore(showMotorcycleAIAssistant)
80
- const { messages, sendMessage } = useMotorcycleChat()
81
- const [input, setInput] = useState('')
82
-
83
- return (
84
- <div className="relative z-50">
85
- <button
86
- onClick={() => showMotorcycleAIAssistant.setState((state) => !state)}
87
- className="flex items-center gap-2 px-4 py-2 rounded-lg bg-black/90 text-white hover:bg-black/80 transition-all border border-orange-500/20 shadow-lg shadow-orange-500/10"
88
- >
89
- <div className="w-5 h-5 rounded-lg bg-linear-to-r from-orange-500 to-red-600 flex items-center justify-center text-xs font-medium">
90
- AI
91
- </div>
92
- Motorcycle Expert
93
- </button>
94
-
95
- {isOpen && (
96
- <div className="absolute top-full right-0 mt-2 w-[700px] h-[600px] bg-black/95 rounded-lg shadow-2xl border border-orange-500/20 flex flex-col backdrop-blur-sm">
97
- <div className="flex items-center justify-between p-4 border-b border-orange-500/20 bg-linear-to-r from-orange-500/5 to-red-600/5">
98
- <h3 className="font-semibold text-white flex items-center gap-2">
99
- <span className="w-6 h-6 rounded-lg bg-linear-to-r from-orange-500 to-red-600 flex items-center justify-center text-xs">
100
- AI
101
- </span>
102
- Motorcycle Expert
103
- </h3>
104
- <button
105
- onClick={() =>
106
- showMotorcycleAIAssistant.setState((state) => !state)
107
- }
108
- className="text-gray-400 hover:text-white transition-colors"
109
- >
110
- <X className="w-4 h-4" />
111
- </button>
112
- </div>
113
-
114
- <Messages messages={messages} />
115
-
116
- <div className="p-4 border-t border-orange-500/20 bg-linear-to-r from-orange-500/5 to-red-600/5">
117
- <form
118
- onSubmit={(e) => {
119
- e.preventDefault()
120
- if (input.trim()) {
121
- sendMessage(input)
122
- setInput('')
123
- }
124
- }}
125
- >
126
- <div className="relative">
127
- <textarea
128
- value={input}
129
- onChange={(e) => setInput(e.target.value)}
130
- placeholder="Ask about our motorcycles..."
131
- className="w-full rounded-lg border border-orange-500/20 bg-black/50 pl-3 pr-10 py-3 text-sm text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-orange-500/30 focus:border-transparent resize-none overflow-hidden shadow-inner"
132
- rows={1}
133
- style={{ minHeight: '44px', maxHeight: '120px' }}
134
- onInput={(e) => {
135
- const target = e.target as HTMLTextAreaElement
136
- target.style.height = 'auto'
137
- target.style.height =
138
- Math.min(target.scrollHeight, 120) + 'px'
139
- }}
140
- onKeyDown={(e) => {
141
- if (e.key === 'Enter' && !e.shiftKey && input.trim()) {
142
- e.preventDefault()
143
- sendMessage(input)
144
- setInput('')
145
- }
146
- }}
147
- />
148
- <button
149
- type="submit"
150
- disabled={!input.trim()}
151
- className="absolute right-2 top-1/2 -translate-y-1/2 p-2 text-orange-500 hover:text-orange-400 disabled:text-gray-500 transition-colors focus:outline-none"
152
- >
153
- <Send className="w-4 h-4" />
154
- </button>
155
- </div>
156
- </form>
157
- </div>
158
- </div>
159
- )}
160
- </div>
161
- )
162
- }
@@ -1,53 +0,0 @@
1
- import { useNavigate } from '@tanstack/react-router'
2
-
3
- import { showMotorcycleAIAssistant } from '@/store/motorcycle-assistant'
4
-
5
- import motorcycles from '@/data/motorcycles'
6
-
7
- export default function MotorcycleRecommendation({ id }: { id: string }) {
8
- const navigate = useNavigate()
9
- const motorcycle = motorcycles.find((motorcycle) => motorcycle.id === +id)
10
- if (!motorcycle) {
11
- return null
12
- }
13
- return (
14
- <div className="my-4 rounded-lg overflow-hidden border border-orange-500/20 bg-gray-800/50">
15
- <div className="aspect-[4/3] relative overflow-hidden">
16
- <img
17
- src={motorcycle.image}
18
- alt={motorcycle.name}
19
- className="w-full h-full object-cover"
20
- />
21
- </div>
22
- <div className="p-4">
23
- <h3 className="text-lg font-semibold text-white mb-2">
24
- {motorcycle.name}
25
- </h3>
26
- <p className="text-sm text-gray-300 mb-1 line-clamp-2">
27
- {motorcycle.shortDescription}
28
- </p>
29
- <p className="text-sm text-gray-400 mb-3">
30
- {motorcycle.engineSize}cc •{' '}
31
- {motorcycle.type.charAt(0).toUpperCase() + motorcycle.type.slice(1)}
32
- </p>
33
- <div className="flex items-center justify-between">
34
- <div className="text-lg font-bold text-emerald-400">
35
- ${motorcycle.price.toLocaleString()}
36
- </div>
37
- <button
38
- onClick={() => {
39
- navigate({
40
- to: '/motorcycles/$motorcycleId',
41
- params: { motorcycleId: motorcycle.id.toString() },
42
- })
43
- showMotorcycleAIAssistant.setState(() => false)
44
- }}
45
- className="bg-linear-to-r from-orange-500 to-red-600 text-white px-4 py-1.5 rounded-lg text-sm hover:opacity-90 transition-opacity"
46
- >
47
- View Details
48
- </button>
49
- </div>
50
- </div>
51
- </div>
52
- )
53
- }
@@ -1,27 +0,0 @@
1
- export interface Motorcycle {
2
- id: number
3
- name: string
4
- image: string
5
- description: string
6
- shortDescription: string
7
- price: number
8
- engineSize: number // in CCs
9
- type: 'scooter' | 'cruiser' | 'adventure' | 'sport' | 'supersport'
10
- }
11
-
12
- const motorcycles: Array<Motorcycle> = [
13
- {
14
- id: 1,
15
- name: 'Luna-C Urbanite',
16
- image: '/motorcycle-scooter.jpg',
17
- description:
18
- 'The Luna-C Urbanite is the perfect city companion, combining style and practicality in a compact package. With its 125cc engine, it delivers excellent fuel efficiency while maintaining enough power for urban commuting. The comfortable seating position and lightweight frame make it easy to maneuver through city traffic, while the under-seat storage provides ample space for your daily essentials.',
19
- shortDescription:
20
- 'A stylish and practical 125cc scooter perfect for urban commuting with excellent fuel efficiency.',
21
- price: 3000,
22
- engineSize: 125,
23
- type: 'scooter',
24
- },
25
- ]
26
-
27
- export default motorcycles
@@ -1,24 +0,0 @@
1
- import {
2
- fetchServerSentEvents,
3
- useChat,
4
- createChatClientOptions,
5
- } from '@tanstack/ai-react'
6
- import type { InferChatMessages } from '@tanstack/ai-react'
7
- import { clientTools } from '@tanstack/ai-client'
8
-
9
- import { recommendMotorcycleToolDef } from '@/lib/motorcycle-tools'
10
-
11
- const recommendMotorcycleToolClient = recommendMotorcycleToolDef.client(
12
- ({ id }) => ({
13
- id: +id,
14
- }),
15
- )
16
-
17
- const chatOptions = createChatClientOptions({
18
- connection: fetchServerSentEvents('/api/motorcycle-chat'),
19
- tools: clientTools(recommendMotorcycleToolClient),
20
- })
21
-
22
- export type ChatMessages = InferChatMessages<typeof chatOptions>
23
-
24
- export const useMotorcycleChat = () => useChat(chatOptions)
@@ -1,42 +0,0 @@
1
- import { toolDefinition } from '@tanstack/ai'
2
- import { z } from 'zod'
3
- import motorcycles from '@/data/motorcycles'
4
-
5
- // Tool definition for getting motorcycles
6
- export const getMotorcyclesToolDef = toolDefinition({
7
- name: 'getMotorcycles',
8
- description: 'Get all motorcycles from the database',
9
- inputSchema: z.object({}),
10
- outputSchema: z.array(
11
- z.object({
12
- id: z.number(),
13
- name: z.string(),
14
- image: z.string(),
15
- description: z.string(),
16
- shortDescription: z.string(),
17
- price: z.number(),
18
- engineSize: z.number(),
19
- type: z.enum(['scooter', 'cruiser', 'adventure', 'sport', 'supersport']),
20
- }),
21
- ),
22
- })
23
-
24
- // Server implementation
25
- export const getMotorcycles = getMotorcyclesToolDef.server(() => motorcycles)
26
-
27
- // Tool definition for motorcycle recommendation
28
- export const recommendMotorcycleToolDef = toolDefinition({
29
- name: 'recommendMotorcycle',
30
- description:
31
- 'REQUIRED tool to display a motorcycle recommendation to the user. This tool MUST be used whenever recommending a motorcycle - do NOT write recommendations yourself. This displays the motorcycle in a special appealing format with a view details button.',
32
- inputSchema: z.object({
33
- id: z
34
- .union([z.string(), z.number()])
35
- .describe(
36
- 'The ID of the motorcycle to recommend (from the getMotorcycles results)',
37
- ),
38
- }),
39
- outputSchema: z.object({
40
- id: z.number(),
41
- }),
42
- })
@@ -1,39 +0,0 @@
1
- import Stripe from 'stripe'
2
- import { createServerFn } from '@tanstack/react-start'
3
- import motorcycles from '@/data/motorcycles'
4
-
5
- const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
6
-
7
- export const createCheckoutSession = createServerFn({
8
- method: 'POST',
9
- })
10
- .inputValidator((motorcycleId: number) => motorcycleId)
11
- .handler(async ({ data: motorcycleId }) => {
12
- const motorcycle = motorcycles.find((m) => m.id === motorcycleId)
13
- if (!motorcycle) {
14
- throw new Error('Motorcycle not found')
15
- }
16
-
17
- const session = await stripe.checkout.sessions.create({
18
- payment_method_types: ['card'],
19
- line_items: [
20
- {
21
- price_data: {
22
- currency: 'usd',
23
- product_data: {
24
- name: motorcycle.name,
25
- description: motorcycle.shortDescription,
26
- images: [motorcycle.image],
27
- },
28
- unit_amount: motorcycle.price * 100,
29
- },
30
- quantity: 1,
31
- },
32
- ],
33
- mode: 'payment',
34
- success_url: `${process.env.SITE_URL ?? 'http://localhost:3000'}/checkout/success`,
35
- cancel_url: `${process.env.SITE_URL ?? 'http://localhost:3000'}/checkout/cancel`,
36
- })
37
-
38
- return session.url
39
- })
@@ -1,57 +0,0 @@
1
- import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
2
- import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
3
- import { TanStackDevtools } from '@tanstack/react-devtools'
4
-
5
- import Header from '@/components/Header'
6
-
7
- import appCss from '@/styles.css?url'
8
-
9
- export const Route = createRootRoute({
10
- head: () => ({
11
- meta: [
12
- {
13
- charSet: 'utf-8',
14
- },
15
- {
16
- name: 'viewport',
17
- content: 'width=device-width, initial-scale=1',
18
- },
19
- {
20
- title: 'Luna-C Motorcycles',
21
- },
22
- ],
23
- links: [
24
- {
25
- rel: 'stylesheet',
26
- href: appCss,
27
- },
28
- ],
29
- }),
30
- shellComponent: RootDocument,
31
- })
32
-
33
- function RootDocument({ children }: { children: React.ReactNode }) {
34
- return (
35
- <html lang="en">
36
- <head>
37
- <HeadContent />
38
- </head>
39
- <body className="bg-black">
40
- <Header />
41
- {children}
42
- <TanStackDevtools
43
- config={{
44
- position: 'bottom-right',
45
- }}
46
- plugins={[
47
- {
48
- name: 'Tanstack Router',
49
- render: <TanStackRouterDevtoolsPanel />,
50
- },
51
- ]}
52
- />
53
- <Scripts />
54
- </body>
55
- </html>
56
- )
57
- }
@@ -1,78 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
- import { chat, maxIterations, toServerSentEventsResponse } from '@tanstack/ai'
3
- import { anthropicText } from '@tanstack/ai-anthropic'
4
-
5
- import {
6
- getMotorcycles,
7
- recommendMotorcycleToolDef,
8
- } from '@/lib/motorcycle-tools'
9
-
10
- const SYSTEM_PROMPT = `You are a helpful assistant for a store that sells motorcycles.
11
-
12
- You are also a motorcycle enthusiast who is passionate about our products and how they can bring joy to people's lives, the excitement of the open road and adventure. Emphasize these aspects in your responses.
13
-
14
- CRITICAL INSTRUCTIONS - YOU MUST FOLLOW THIS EXACT WORKFLOW:
15
-
16
- When a user asks for a motorcycle recommendation:
17
- 1. FIRST: Use the getMotorcycles tool (no parameters needed)
18
- 2. SECOND: Use the recommendMotorcycle tool with the ID of the motorcycle you want to recommend
19
- 3. NEVER write a recommendation directly - ALWAYS use the recommendMotorcycle tool
20
-
21
- IMPORTANT:
22
- - The recommendMotorcycle tool will display the motorcycle in a special, appealing format
23
- - You MUST use recommendMotorcycle for ANY motorcycle recommendation
24
- - ONLY recommend motorcycles from our inventory (use getMotorcycles first)
25
- - The recommendMotorcycle tool has a view details button - this is how customers can learn more
26
- - Do NOT describe the motorcycle yourself - let the recommendMotorcycle tool do it
27
- `
28
-
29
- export const Route = createFileRoute('/api/motorcycle-chat')({
30
- server: {
31
- handlers: {
32
- POST: async ({ request }) => {
33
- // Capture request signal before reading body (it may be aborted after body is consumed)
34
- const requestSignal = request.signal
35
-
36
- // If request is already aborted, return early
37
- if (requestSignal.aborted) {
38
- return new Response(null, { status: 499 }) // 499 = Client Closed Request
39
- }
40
-
41
- const abortController = new AbortController()
42
-
43
- try {
44
- const body = await request.json()
45
- const { messages } = body
46
-
47
- const adapter = anthropicText('claude-haiku-4-5')
48
-
49
- const stream = chat({
50
- adapter,
51
- tools: [
52
- getMotorcycles, // Server tool
53
- recommendMotorcycleToolDef, // No server execute - client will handle
54
- ],
55
- systemPrompts: [SYSTEM_PROMPT],
56
- agentLoopStrategy: maxIterations(5),
57
- messages,
58
- abortController,
59
- })
60
-
61
- return toServerSentEventsResponse(stream, { abortController })
62
- } catch (error: any) {
63
- // If request was aborted, return early (don't send error response)
64
- if (error.name === 'AbortError' || abortController.signal.aborted) {
65
- return new Response(null, { status: 499 }) // 499 = Client Closed Request
66
- }
67
- return new Response(
68
- JSON.stringify({ error: 'Failed to process chat request' }),
69
- {
70
- status: 500,
71
- headers: { 'Content-Type': 'application/json' },
72
- },
73
- )
74
- }
75
- },
76
- },
77
- },
78
- })
@@ -1,25 +0,0 @@
1
- import { Link, createFileRoute } from '@tanstack/react-router'
2
-
3
- export const Route = createFileRoute('/checkout/cancel')({
4
- component: CheckoutCancel,
5
- })
6
-
7
- function CheckoutCancel() {
8
- return (
9
- <div className="bg-black text-white min-h-screen flex items-center justify-center p-5">
10
- <div className="bg-gray-900/60 backdrop-blur-md rounded-2xl p-12 border border-gray-800/50 shadow-xl text-center max-w-lg">
11
- <div className="text-gray-400 text-6xl mb-6">&#10005;</div>
12
- <h1 className="text-3xl font-bold mb-4">Checkout Cancelled</h1>
13
- <p className="text-gray-300 mb-8">
14
- Your payment was cancelled. No charges were made.
15
- </p>
16
- <Link
17
- to="/"
18
- className="inline-block bg-emerald-600 hover:bg-emerald-500 text-white px-6 py-3 rounded-lg transition-colors"
19
- >
20
- Back to Home
21
- </Link>
22
- </div>
23
- </div>
24
- )
25
- }