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,61 +0,0 @@
1
- import { useState, useEffect } from 'react'
2
-
3
- const backgrounds = [
4
- '/background-1.jpg',
5
- '/background-2.jpg',
6
- '/background-3.jpg',
7
- '/background-4.jpg',
8
- ]
9
-
10
- export default function HeroCarousel() {
11
- const [currentIndex, setCurrentIndex] = useState(0)
12
-
13
- useEffect(() => {
14
- const interval = setInterval(() => {
15
- setCurrentIndex((prev) => (prev + 1) % backgrounds.length)
16
- }, 6000)
17
- return () => clearInterval(interval)
18
- }, [])
19
-
20
- return (
21
- <div className="absolute inset-0 overflow-hidden">
22
- {/* Background images with crossfade */}
23
- {backgrounds.map((bg, index) => (
24
- <div
25
- key={bg}
26
- className={`absolute inset-0 transition-opacity duration-1000 ease-in-out ${
27
- index === currentIndex ? 'opacity-100' : 'opacity-0'
28
- }`}
29
- >
30
- <img
31
- src={bg}
32
- alt=""
33
- className="w-full h-full object-cover"
34
- />
35
- </div>
36
- ))}
37
-
38
- {/* Dark overlay for text readability */}
39
- <div className="absolute inset-0 bg-gradient-to-b from-charcoal/70 via-charcoal/60 to-charcoal" />
40
-
41
- {/* Subtle vignette effect */}
42
- <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,transparent_0%,rgba(26,26,26,0.4)_100%)]" />
43
-
44
- {/* Carousel indicators */}
45
- <div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex gap-2 z-10">
46
- {backgrounds.map((_, index) => (
47
- <button
48
- key={index}
49
- onClick={() => setCurrentIndex(index)}
50
- className={`w-2 h-2 rounded-full transition-all duration-300 ${
51
- index === currentIndex
52
- ? 'bg-gold w-6'
53
- : 'bg-cream/30 hover:bg-cream/50'
54
- }`}
55
- aria-label={`Go to slide ${index + 1}`}
56
- />
57
- ))}
58
- </div>
59
- </div>
60
- )
61
- }
@@ -1,207 +0,0 @@
1
- import { useEffect, useRef, useState } from 'react'
2
- import { Send, X, ChefHat, Croissant } from 'lucide-react'
3
- import { Streamdown } from 'streamdown'
4
- import { Store } from '@tanstack/store'
5
-
6
- import { useConferenceChat } from '@/lib/conference-ai-hook'
7
- import type { ConferenceChatMessages } from '@/lib/conference-ai-hook'
8
-
9
- function Messages({ messages }: { messages: ConferenceChatMessages }) {
10
- const messagesContainerRef = useRef<HTMLDivElement>(null)
11
-
12
- useEffect(() => {
13
- if (messagesContainerRef.current) {
14
- messagesContainerRef.current.scrollTop =
15
- messagesContainerRef.current.scrollHeight
16
- }
17
- }, [messages])
18
-
19
- if (!messages.length) {
20
- return (
21
- <div className="flex-1 flex flex-col items-center justify-center text-cream/60 text-sm px-6 py-8">
22
- <div className="relative mb-4">
23
- <ChefHat className="w-12 h-12 text-copper/60 animate-pulse" />
24
- <Croissant className="w-6 h-6 text-gold/60 absolute -bottom-1 -right-1" />
25
- </div>
26
- <p className="text-center text-cream/80 font-medium font-display text-lg">
27
- Bonjour! I'm Remy 👨‍🍳
28
- </p>
29
- <p className="text-xs text-cream/40 mt-2 text-center max-w-[220px]">
30
- Your culinary guide to Haute Pâtisserie 2026. Ask about speakers, sessions, or pastry techniques!
31
- </p>
32
- </div>
33
- )
34
- }
35
-
36
- return (
37
- <div ref={messagesContainerRef} className="flex-1 overflow-y-auto">
38
- {messages.map(({ id, role, parts }) => (
39
- <div
40
- key={id}
41
- className={`py-3 ${
42
- role === 'assistant'
43
- ? 'bg-gradient-to-r from-copper/5 via-gold/5 to-copper/5'
44
- : 'bg-transparent'
45
- }`}
46
- >
47
- {parts.map((part, index) => {
48
- if (part.type === 'text' && part.content) {
49
- return (
50
- <div key={index} className="flex items-start gap-3 px-4">
51
- {role === 'assistant' ? (
52
- <div className="w-7 h-7 rounded-full bg-gradient-to-br from-copper via-copper-dark to-gold flex items-center justify-center text-xs font-bold text-charcoal flex-shrink-0 shadow-lg shadow-copper/20">
53
- 👨‍🍳
54
- </div>
55
- ) : (
56
- <div className="w-7 h-7 rounded-full bg-charcoal-light flex items-center justify-center text-xs font-medium text-cream flex-shrink-0 border border-border/50">
57
- You
58
- </div>
59
- )}
60
- <div className="flex-1 min-w-0 text-cream prose dark:prose-invert max-w-none prose-sm prose-p:text-cream prose-headings:text-cream prose-strong:text-gold">
61
- <Streamdown>{part.content}</Streamdown>
62
- </div>
63
- </div>
64
- )
65
- }
66
- return null
67
- })}
68
- </div>
69
- ))}
70
- </div>
71
- )
72
- }
73
-
74
- interface RemyAssistantProps {
75
- speakerSlug?: string
76
- talkSlug?: string
77
- contextTitle?: string
78
- }
79
-
80
- // Export store for header control
81
- export const showRemyAssistant = new Store(false)
82
-
83
- export default function RemyAssistant({
84
- speakerSlug,
85
- talkSlug,
86
- contextTitle,
87
- }: RemyAssistantProps) {
88
- const [isOpen, setIsOpen] = useState(false)
89
- const { messages, sendMessage, isLoading } = useConferenceChat(speakerSlug, talkSlug)
90
- const [input, setInput] = useState('')
91
-
92
- // Sync with store for header control
93
- useEffect(() => {
94
- return showRemyAssistant.subscribe(() => {
95
- setIsOpen(showRemyAssistant.state)
96
- })
97
- }, [])
98
-
99
- const handleToggle = () => {
100
- const newState = !isOpen
101
- setIsOpen(newState)
102
- showRemyAssistant.setState(() => newState)
103
- }
104
-
105
- const handleSend = () => {
106
- if (input.trim()) {
107
- sendMessage(input)
108
- setInput('')
109
- }
110
- }
111
-
112
- if (!isOpen) return null
113
-
114
- return (
115
- <div className="fixed top-36 right-4 z-[100] w-[400px] h-[520px] rounded-2xl shadow-2xl flex flex-col overflow-hidden border border-copper/20 backdrop-blur-xl bg-gradient-to-b from-charcoal/98 via-charcoal/95 to-charcoal-light/98">
116
- {/* Decorative top gradient */}
117
- <div className="absolute top-0 left-0 right-0 h-32 bg-gradient-to-b from-copper/10 via-gold/5 to-transparent pointer-events-none" />
118
-
119
- {/* Header */}
120
- <div className="relative flex items-center justify-between p-4 border-b border-copper/10">
121
- <div className="flex items-center gap-3">
122
- <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-copper via-copper-dark to-gold flex items-center justify-center shadow-lg shadow-copper/30 rotate-3 hover:rotate-0 transition-transform">
123
- <span className="text-lg">👨‍🍳</span>
124
- </div>
125
- <div>
126
- <h3 className="font-display font-bold text-cream text-base tracking-tight">
127
- Remy
128
- </h3>
129
- {contextTitle && (
130
- <p className="text-xs text-copper/70 truncate max-w-[220px]">
131
- 🥐 {contextTitle}
132
- </p>
133
- )}
134
- </div>
135
- </div>
136
- <button
137
- onClick={handleToggle}
138
- className="text-cream/50 hover:text-cream transition-colors p-2 hover:bg-white/5 rounded-xl"
139
- >
140
- <X className="w-5 h-5" />
141
- </button>
142
- </div>
143
-
144
- {/* Messages */}
145
- <Messages messages={messages} />
146
-
147
- {/* Loading indicator */}
148
- {isLoading && (
149
- <div className="px-4 py-3 border-t border-copper/10">
150
- <div className="flex items-center gap-2 text-copper/80 text-xs">
151
- <div className="flex gap-1">
152
- <span className="w-2 h-2 bg-copper rounded-full animate-bounce [animation-delay:-0.3s]"></span>
153
- <span className="w-2 h-2 bg-gold rounded-full animate-bounce [animation-delay:-0.15s]"></span>
154
- <span className="w-2 h-2 bg-copper-light rounded-full animate-bounce"></span>
155
- </div>
156
- <span className="font-medium">Crafting a response...</span>
157
- </div>
158
- </div>
159
- )}
160
-
161
- {/* Input */}
162
- <div className="relative p-4 border-t border-copper/10 bg-charcoal/50">
163
- <form
164
- onSubmit={(e) => {
165
- e.preventDefault()
166
- handleSend()
167
- }}
168
- >
169
- <div className="relative">
170
- <textarea
171
- value={input}
172
- onChange={(e) => setInput(e.target.value)}
173
- placeholder="Ask about speakers, sessions, techniques..."
174
- disabled={isLoading}
175
- className="w-full rounded-xl border border-copper/20 bg-charcoal-light/50 pl-4 pr-12 py-3 text-sm text-cream placeholder-cream/30 focus:outline-none focus:ring-2 focus:ring-copper/40 focus:border-transparent resize-none overflow-hidden disabled:opacity-50 transition-all"
176
- rows={1}
177
- style={{ minHeight: '48px', maxHeight: '100px' }}
178
- onInput={(e) => {
179
- const target = e.target as HTMLTextAreaElement
180
- target.style.height = 'auto'
181
- target.style.height = Math.min(target.scrollHeight, 100) + 'px'
182
- }}
183
- onKeyDown={(e) => {
184
- if (
185
- e.key === 'Enter' &&
186
- !e.shiftKey &&
187
- input.trim() &&
188
- !isLoading
189
- ) {
190
- e.preventDefault()
191
- handleSend()
192
- }
193
- }}
194
- />
195
- <button
196
- type="submit"
197
- disabled={!input.trim() || isLoading}
198
- className="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-lg bg-gradient-to-r from-copper to-copper-dark text-charcoal disabled:opacity-30 disabled:bg-gray-600 disabled:from-gray-600 disabled:to-gray-600 transition-all hover:shadow-lg hover:shadow-copper/20"
199
- >
200
- <Send className="w-4 h-4" />
201
- </button>
202
- </div>
203
- </form>
204
- </div>
205
- </div>
206
- )
207
- }
@@ -1,67 +0,0 @@
1
- import { Link } from '@tanstack/react-router'
2
- import { MapPin } from 'lucide-react'
3
-
4
- import { type Speaker } from 'content-collections'
5
-
6
- import { Card, CardContent } from '@/components/ui/card'
7
-
8
- interface SpeakerCardProps {
9
- speaker: Speaker
10
- featured?: boolean
11
- }
12
-
13
- export default function SpeakerCard({ speaker, featured = false }: SpeakerCardProps) {
14
- return (
15
- <Link
16
- to={`/speakers/${speaker.slug}`}
17
- className="group relative block"
18
- >
19
- <Card
20
- className={`relative overflow-hidden bg-card border-border/50 card-hover
21
- ${featured ? 'aspect-square' : 'aspect-square'}
22
- hover:border-copper/50`}
23
- >
24
- {/* Headshot */}
25
- <div className="absolute inset-0">
26
- <img
27
- src={`/${speaker.headshot}`}
28
- alt={speaker.name}
29
- className="w-full h-full object-cover"
30
- />
31
- <div className="absolute inset-0 bg-gradient-to-t from-charcoal via-charcoal/50 to-transparent" />
32
- </div>
33
-
34
- {/* Content overlay */}
35
- <CardContent className="absolute bottom-0 left-0 right-0 p-6 z-10">
36
- <div className="space-y-2">
37
- {/* Specialty tag */}
38
- <span className="inline-block px-3 py-1 text-xs font-medium tracking-wider uppercase bg-copper/20 text-copper-light rounded-full border border-copper/30">
39
- {speaker.specialty}
40
- </span>
41
-
42
- {/* Name */}
43
- <h3 className="font-display text-2xl font-semibold text-cream group-hover:text-gold transition-colors">
44
- {speaker.name}
45
- </h3>
46
-
47
- {/* Title & Restaurant */}
48
- <p className="text-cream/70 font-body text-lg">
49
- {speaker.title}
50
- </p>
51
-
52
- {/* Location */}
53
- <div className="flex items-center gap-2 text-cream/50 text-sm">
54
- <MapPin className="w-3.5 h-3.5" />
55
- <span>{speaker.restaurant}, {speaker.location}</span>
56
- </div>
57
- </div>
58
- </CardContent>
59
-
60
- {/* Decorative corner accent */}
61
- <div className="absolute top-0 right-0 w-20 h-20 overflow-hidden">
62
- <div className="absolute top-0 right-0 w-28 h-28 bg-gradient-to-bl from-copper/20 to-transparent transform rotate-45 translate-x-14 -translate-y-14" />
63
- </div>
64
- </Card>
65
- </Link>
66
- )
67
- }
@@ -1,77 +0,0 @@
1
- import { Link } from '@tanstack/react-router'
2
- import { Clock, User } from 'lucide-react'
3
-
4
- import { type Talk } from 'content-collections'
5
-
6
- import { Card, CardContent } from '@/components/ui/card'
7
-
8
- interface TalkCardProps {
9
- talk: Talk
10
- featured?: boolean
11
- }
12
-
13
- export default function TalkCard({ talk, featured = false }: TalkCardProps) {
14
- return (
15
- <Link
16
- to={`/talks/${talk.slug}`}
17
- className="group relative block"
18
- >
19
- <Card
20
- className={`relative overflow-hidden bg-card border-border/50 card-hover
21
- ${featured ? 'aspect-[16/10]' : 'aspect-[16/9]'}
22
- hover:border-gold/50`}
23
- >
24
- {/* Image */}
25
- <div className="absolute inset-0">
26
- <img
27
- src={`/${talk.image}`}
28
- alt={talk.title}
29
- className="w-full h-full object-cover"
30
- />
31
- <div className="absolute inset-0 bg-gradient-to-t from-charcoal via-charcoal/60 to-transparent" />
32
- </div>
33
-
34
- {/* Content overlay */}
35
- <CardContent className="absolute bottom-0 left-0 right-0 p-6 z-10">
36
- <div className="space-y-3">
37
- {/* Topics */}
38
- <div className="flex flex-wrap gap-2">
39
- {talk.topics.slice(0, 2).map((topic) => (
40
- <span
41
- key={topic}
42
- className="px-2.5 py-0.5 text-xs font-medium tracking-wide uppercase bg-gold/15 text-gold border border-gold/30 rounded-full"
43
- >
44
- {topic}
45
- </span>
46
- ))}
47
- </div>
48
-
49
- {/* Title */}
50
- <h3 className="font-display text-xl font-semibold text-cream group-hover:text-gold transition-colors leading-tight">
51
- {talk.title}
52
- </h3>
53
-
54
- {/* Speaker & Duration */}
55
- <div className="flex items-center gap-4 text-cream/60 text-sm">
56
- <div className="flex items-center gap-1.5">
57
- <User className="w-3.5 h-3.5" />
58
- <span>{talk.speaker}</span>
59
- </div>
60
- <div className="flex items-center gap-1.5">
61
- <Clock className="w-3.5 h-3.5" />
62
- <span>{talk.duration}</span>
63
- </div>
64
- </div>
65
- </div>
66
- </CardContent>
67
-
68
- {/* Decorative accent */}
69
- <div className="absolute top-4 right-4">
70
- <div className="w-8 h-8 rounded-full bg-gold/10 border border-gold/20 flex items-center justify-center">
71
- <span className="text-gold/60 text-xs font-display">✦</span>
72
- </div>
73
- </div>
74
- </Card>
75
- </Link>
76
- )
77
- }
@@ -1,92 +0,0 @@
1
- import * as React from 'react'
2
-
3
- import { cn } from '@/lib/utils'
4
-
5
- function Card({ className, ...props }: React.ComponentProps<'div'>) {
6
- return (
7
- <div
8
- data-slot="card"
9
- className={cn(
10
- 'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
11
- className,
12
- )}
13
- {...props}
14
- />
15
- )
16
- }
17
-
18
- function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
19
- return (
20
- <div
21
- data-slot="card-header"
22
- className={cn(
23
- '@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
24
- className,
25
- )}
26
- {...props}
27
- />
28
- )
29
- }
30
-
31
- function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
32
- return (
33
- <div
34
- data-slot="card-title"
35
- className={cn('leading-none font-semibold', className)}
36
- {...props}
37
- />
38
- )
39
- }
40
-
41
- function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
42
- return (
43
- <div
44
- data-slot="card-description"
45
- className={cn('text-muted-foreground text-sm', className)}
46
- {...props}
47
- />
48
- )
49
- }
50
-
51
- function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
52
- return (
53
- <div
54
- data-slot="card-action"
55
- className={cn(
56
- 'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
57
- className,
58
- )}
59
- {...props}
60
- />
61
- )
62
- }
63
-
64
- function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
65
- return (
66
- <div
67
- data-slot="card-content"
68
- className={cn('px-6', className)}
69
- {...props}
70
- />
71
- )
72
- }
73
-
74
- function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
75
- return (
76
- <div
77
- data-slot="card-footer"
78
- className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
79
- {...props}
80
- />
81
- )
82
- }
83
-
84
- export {
85
- Card,
86
- CardHeader,
87
- CardFooter,
88
- CardTitle,
89
- CardAction,
90
- CardDescription,
91
- CardContent,
92
- }
@@ -1,26 +0,0 @@
1
- import {
2
- fetchServerSentEvents,
3
- useChat,
4
- createChatClientOptions,
5
- } from '@tanstack/ai-react'
6
- import type { InferChatMessages } from '@tanstack/ai-react'
7
-
8
- // Default chat options for type inference
9
- const defaultChatOptions = createChatClientOptions({
10
- connection: fetchServerSentEvents('/api/remy-chat'),
11
- })
12
-
13
- export type ConferenceChatMessages = InferChatMessages<typeof defaultChatOptions>
14
-
15
- export const useConferenceChat = (speakerSlug?: string, talkSlug?: string) => {
16
- const chatOptions = createChatClientOptions({
17
- connection: fetchServerSentEvents('/api/remy-chat', {
18
- body: {
19
- speakerSlug,
20
- talkSlug,
21
- },
22
- }),
23
- })
24
-
25
- return useChat(chatOptions)
26
- }