@sundaysf/cli-v2 1.0.1 → 1.0.5

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 (191) hide show
  1. package/README.md +178 -178
  2. package/dist/README.md +178 -178
  3. package/dist/bin/generators/class.js.map +1 -1
  4. package/dist/bin/generators/postman.js.map +1 -1
  5. package/dist/bin/index.js +1 -1
  6. package/dist/bin/index.js.map +1 -1
  7. package/dist/templates/backend/.claude/agents/knex-table-implementer.md +113 -113
  8. package/dist/templates/backend/.claude/agents/sundays-backend-builder.md +70 -70
  9. package/dist/templates/backend/.claude/settings.local.json +13 -13
  10. package/dist/templates/backend/.env.example +13 -13
  11. package/dist/templates/backend/.prettierignore +2 -2
  12. package/dist/templates/backend/.prettierrc +9 -9
  13. package/dist/templates/backend/.sundaysrc +7 -0
  14. package/dist/templates/backend/CLAUDE.md +348 -348
  15. package/dist/templates/backend/Dockerfile +14 -14
  16. package/dist/templates/backend/README.md +18 -18
  17. package/dist/templates/backend/eslint.config.js +20 -20
  18. package/dist/templates/backend/src/app.ts +34 -34
  19. package/dist/templates/backend/src/common/config/origins/origins.config.ts +11 -11
  20. package/dist/templates/backend/src/common/utils/environment.resolver.ts +3 -3
  21. package/dist/templates/backend/src/common/utils/version.resolver.ts +4 -4
  22. package/dist/templates/backend/src/controllers/health/health.controller.ts +23 -23
  23. package/dist/templates/backend/src/middlewares/error/error.middleware.ts +21 -21
  24. package/dist/templates/backend/src/routes/health/health.router.ts +16 -16
  25. package/dist/templates/backend/src/routes/index.ts +57 -57
  26. package/dist/templates/backend/src/server.ts +16 -16
  27. package/dist/templates/backend/src/types.d.ts +10 -10
  28. package/dist/templates/backend/tsconfig.json +16 -16
  29. package/dist/templates/backend-db-sql/.claude/agents/knex-table-implementer.md +114 -114
  30. package/dist/templates/backend-db-sql/.claude/agents/sundays-backend-builder.md +70 -70
  31. package/dist/templates/backend-db-sql/.claude/settings.local.json +19 -19
  32. package/dist/templates/backend-db-sql/.env.example +13 -13
  33. package/dist/templates/backend-db-sql/.prettierignore +2 -2
  34. package/dist/templates/backend-db-sql/.prettierrc +9 -9
  35. package/dist/templates/backend-db-sql/.sundaysrc +7 -0
  36. package/dist/templates/backend-db-sql/CLAUDE.md +374 -374
  37. package/dist/templates/backend-db-sql/Dockerfile +17 -17
  38. package/dist/templates/backend-db-sql/README.md +34 -34
  39. package/dist/templates/backend-db-sql/db/knexfile.ts +33 -33
  40. package/dist/templates/backend-db-sql/db/migrations/001_create_sundays_package_version.ts +12 -12
  41. package/dist/templates/backend-db-sql/db/seeds/001_sundays_package_version_seed.ts +10 -10
  42. package/dist/templates/backend-db-sql/db/src/KnexConnection.ts +74 -74
  43. package/dist/templates/backend-db-sql/db/src/d.types.ts +18 -18
  44. package/dist/templates/backend-db-sql/db/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
  45. package/dist/templates/backend-db-sql/db/src/index.ts +9 -9
  46. package/dist/templates/backend-db-sql/db/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
  47. package/dist/templates/backend-db-sql/db/tsconfig.json +16 -16
  48. package/dist/templates/backend-db-sql/eslint.config.js +20 -20
  49. package/dist/templates/backend-db-sql/src/app.ts +34 -34
  50. package/dist/templates/backend-db-sql/src/common/config/origins/origins.config.ts +11 -11
  51. package/dist/templates/backend-db-sql/src/common/utils/environment.resolver.ts +3 -3
  52. package/dist/templates/backend-db-sql/src/common/utils/version.resolver.ts +4 -4
  53. package/dist/templates/backend-db-sql/src/controllers/health/health.controller.ts +23 -23
  54. package/dist/templates/backend-db-sql/src/middlewares/error/error.middleware.ts +21 -21
  55. package/dist/templates/backend-db-sql/src/routes/health/health.router.ts +16 -16
  56. package/dist/templates/backend-db-sql/src/routes/index.ts +57 -57
  57. package/dist/templates/backend-db-sql/src/server.ts +18 -18
  58. package/dist/templates/backend-db-sql/src/types.d.ts +10 -10
  59. package/dist/templates/backend-db-sql/tsconfig.json +16 -16
  60. package/dist/templates/backend-embedded-db-sql/.claude/agents/knex-table-implementer.md +116 -0
  61. package/dist/templates/backend-embedded-db-sql/.claude/agents/sundays-backend-builder.md +70 -0
  62. package/dist/templates/backend-embedded-db-sql/.claude/settings.local.json +18 -0
  63. package/dist/templates/backend-embedded-db-sql/.env.example +14 -0
  64. package/dist/templates/backend-embedded-db-sql/.prettierignore +3 -0
  65. package/dist/templates/backend-embedded-db-sql/.prettierrc +9 -0
  66. package/dist/templates/backend-embedded-db-sql/.sundaysrc +7 -0
  67. package/dist/templates/backend-embedded-db-sql/CLAUDE.md +371 -0
  68. package/dist/templates/backend-embedded-db-sql/Dockerfile +14 -0
  69. package/dist/templates/backend-embedded-db-sql/README.md +32 -0
  70. package/dist/templates/backend-embedded-db-sql/eslint.config.js +20 -0
  71. package/dist/templates/backend-embedded-db-sql/knexfile.ts +37 -0
  72. package/dist/templates/backend-embedded-db-sql/migrations/.gitkeep +0 -0
  73. package/dist/templates/backend-embedded-db-sql/migrations/001_create_sundays_package_version.ts +13 -0
  74. package/dist/templates/backend-embedded-db-sql/seeds/001_sundays_package_version_seed.ts +11 -0
  75. package/dist/templates/backend-embedded-db-sql/src/app.ts +35 -0
  76. package/dist/templates/backend-embedded-db-sql/src/common/config/origins/origins.config.ts +11 -0
  77. package/dist/templates/backend-embedded-db-sql/src/common/utils/environment.resolver.ts +4 -0
  78. package/dist/templates/backend-embedded-db-sql/src/common/utils/version.resolver.ts +5 -0
  79. package/dist/templates/backend-embedded-db-sql/src/controllers/health/health.controller.ts +24 -0
  80. package/dist/templates/backend-embedded-db-sql/src/db/KnexConnection.ts +74 -0
  81. package/dist/templates/backend-embedded-db-sql/src/db/d.types.ts +18 -0
  82. package/dist/templates/backend-embedded-db-sql/src/db/dao/sundays-package-version/sundays-package-version.dao.ts +71 -0
  83. package/dist/templates/backend-embedded-db-sql/src/db/index.ts +9 -0
  84. package/dist/templates/backend-embedded-db-sql/src/db/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -0
  85. package/dist/templates/backend-embedded-db-sql/src/middlewares/error/error.middleware.ts +21 -0
  86. package/dist/templates/backend-embedded-db-sql/src/routes/health/health.router.ts +17 -0
  87. package/dist/templates/backend-embedded-db-sql/src/routes/index.ts +57 -0
  88. package/dist/templates/backend-embedded-db-sql/src/server.ts +18 -0
  89. package/dist/templates/backend-embedded-db-sql/src/types.d.ts +10 -0
  90. package/dist/templates/backend-embedded-db-sql/tsconfig.json +16 -0
  91. package/dist/templates/db-sql/.claude/agents/knex-table-implementer.md +113 -113
  92. package/dist/templates/db-sql/.claude/agents/sundays-backend-builder.md +70 -70
  93. package/dist/templates/db-sql/.claude/settings.local.json +10 -10
  94. package/dist/templates/db-sql/.env.example +8 -8
  95. package/dist/templates/db-sql/CLAUDE.md +105 -105
  96. package/dist/templates/db-sql/knexfile.ts +33 -33
  97. package/dist/templates/db-sql/migrations/001_create_sundays_package_version.ts +12 -12
  98. package/dist/templates/db-sql/seeds/001_sundays_package_version_seed.ts +10 -10
  99. package/dist/templates/db-sql/src/KnexConnection.ts +74 -74
  100. package/dist/templates/db-sql/src/d.types.ts +18 -18
  101. package/dist/templates/db-sql/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
  102. package/dist/templates/db-sql/src/index.ts +9 -9
  103. package/dist/templates/db-sql/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
  104. package/dist/templates/db-sql/tsconfig.json +16 -16
  105. package/dist/templates/frontend-nextjs/.sundaysrc +7 -0
  106. package/dist/templates/frontend-nextjs/app/globals.css +125 -0
  107. package/dist/templates/frontend-nextjs/app/layout.tsx +45 -0
  108. package/dist/templates/frontend-nextjs/app/page.tsx +5 -0
  109. package/dist/templates/frontend-nextjs/components/project-generator.tsx +558 -0
  110. package/dist/templates/frontend-nextjs/components/theme-provider.tsx +11 -0
  111. package/dist/templates/frontend-nextjs/components/ui/accordion.tsx +66 -0
  112. package/dist/templates/frontend-nextjs/components/ui/alert-dialog.tsx +157 -0
  113. package/dist/templates/frontend-nextjs/components/ui/alert.tsx +66 -0
  114. package/dist/templates/frontend-nextjs/components/ui/aspect-ratio.tsx +11 -0
  115. package/dist/templates/frontend-nextjs/components/ui/avatar.tsx +53 -0
  116. package/dist/templates/frontend-nextjs/components/ui/badge.tsx +46 -0
  117. package/dist/templates/frontend-nextjs/components/ui/breadcrumb.tsx +109 -0
  118. package/dist/templates/frontend-nextjs/components/ui/button-group.tsx +83 -0
  119. package/dist/templates/frontend-nextjs/components/ui/button.tsx +60 -0
  120. package/dist/templates/frontend-nextjs/components/ui/calendar.tsx +213 -0
  121. package/dist/templates/frontend-nextjs/components/ui/card.tsx +92 -0
  122. package/dist/templates/frontend-nextjs/components/ui/carousel.tsx +241 -0
  123. package/dist/templates/frontend-nextjs/components/ui/chart.tsx +353 -0
  124. package/dist/templates/frontend-nextjs/components/ui/checkbox.tsx +32 -0
  125. package/dist/templates/frontend-nextjs/components/ui/collapsible.tsx +33 -0
  126. package/dist/templates/frontend-nextjs/components/ui/command.tsx +184 -0
  127. package/dist/templates/frontend-nextjs/components/ui/context-menu.tsx +252 -0
  128. package/dist/templates/frontend-nextjs/components/ui/dialog.tsx +143 -0
  129. package/dist/templates/frontend-nextjs/components/ui/drawer.tsx +135 -0
  130. package/dist/templates/frontend-nextjs/components/ui/dropdown-menu.tsx +257 -0
  131. package/dist/templates/frontend-nextjs/components/ui/empty.tsx +104 -0
  132. package/dist/templates/frontend-nextjs/components/ui/field.tsx +244 -0
  133. package/dist/templates/frontend-nextjs/components/ui/form.tsx +167 -0
  134. package/dist/templates/frontend-nextjs/components/ui/hover-card.tsx +44 -0
  135. package/dist/templates/frontend-nextjs/components/ui/input-group.tsx +169 -0
  136. package/dist/templates/frontend-nextjs/components/ui/input-otp.tsx +77 -0
  137. package/dist/templates/frontend-nextjs/components/ui/input.tsx +21 -0
  138. package/dist/templates/frontend-nextjs/components/ui/item.tsx +193 -0
  139. package/dist/templates/frontend-nextjs/components/ui/kbd.tsx +28 -0
  140. package/dist/templates/frontend-nextjs/components/ui/label.tsx +24 -0
  141. package/dist/templates/frontend-nextjs/components/ui/menubar.tsx +276 -0
  142. package/dist/templates/frontend-nextjs/components/ui/navigation-menu.tsx +166 -0
  143. package/dist/templates/frontend-nextjs/components/ui/pagination.tsx +127 -0
  144. package/dist/templates/frontend-nextjs/components/ui/popover.tsx +48 -0
  145. package/dist/templates/frontend-nextjs/components/ui/progress.tsx +31 -0
  146. package/dist/templates/frontend-nextjs/components/ui/radio-group.tsx +45 -0
  147. package/dist/templates/frontend-nextjs/components/ui/resizable.tsx +56 -0
  148. package/dist/templates/frontend-nextjs/components/ui/scroll-area.tsx +58 -0
  149. package/dist/templates/frontend-nextjs/components/ui/select.tsx +185 -0
  150. package/dist/templates/frontend-nextjs/components/ui/separator.tsx +28 -0
  151. package/dist/templates/frontend-nextjs/components/ui/sheet.tsx +139 -0
  152. package/dist/templates/frontend-nextjs/components/ui/sidebar.tsx +726 -0
  153. package/dist/templates/frontend-nextjs/components/ui/skeleton.tsx +13 -0
  154. package/dist/templates/frontend-nextjs/components/ui/slider.tsx +63 -0
  155. package/dist/templates/frontend-nextjs/components/ui/sonner.tsx +25 -0
  156. package/dist/templates/frontend-nextjs/components/ui/spinner.tsx +16 -0
  157. package/dist/templates/frontend-nextjs/components/ui/switch.tsx +31 -0
  158. package/dist/templates/frontend-nextjs/components/ui/table.tsx +116 -0
  159. package/dist/templates/frontend-nextjs/components/ui/tabs.tsx +66 -0
  160. package/dist/templates/frontend-nextjs/components/ui/textarea.tsx +18 -0
  161. package/dist/templates/frontend-nextjs/components/ui/toast.tsx +129 -0
  162. package/dist/templates/frontend-nextjs/components/ui/toaster.tsx +35 -0
  163. package/dist/templates/frontend-nextjs/components/ui/toggle-group.tsx +73 -0
  164. package/dist/templates/frontend-nextjs/components/ui/toggle.tsx +47 -0
  165. package/dist/templates/frontend-nextjs/components/ui/tooltip.tsx +61 -0
  166. package/dist/templates/frontend-nextjs/components/ui/use-mobile.tsx +19 -0
  167. package/dist/templates/frontend-nextjs/components/ui/use-toast.ts +191 -0
  168. package/dist/templates/frontend-nextjs/components.json +21 -0
  169. package/dist/templates/frontend-nextjs/hooks/use-mobile.ts +19 -0
  170. package/dist/templates/frontend-nextjs/hooks/use-toast.ts +191 -0
  171. package/dist/templates/frontend-nextjs/lib/utils.ts +6 -0
  172. package/dist/templates/frontend-nextjs/next.config.mjs +11 -0
  173. package/dist/templates/frontend-nextjs/postcss.config.mjs +8 -0
  174. package/dist/templates/frontend-nextjs/public/apple-icon.png +0 -0
  175. package/dist/templates/frontend-nextjs/public/icon-dark-32x32.png +0 -0
  176. package/dist/templates/frontend-nextjs/public/icon-light-32x32.png +0 -0
  177. package/dist/templates/frontend-nextjs/public/icon.svg +26 -0
  178. package/dist/templates/frontend-nextjs/public/placeholder-logo.png +0 -0
  179. package/dist/templates/frontend-nextjs/public/placeholder-logo.svg +1 -0
  180. package/dist/templates/frontend-nextjs/public/placeholder-user.jpg +0 -0
  181. package/dist/templates/frontend-nextjs/public/placeholder.jpg +0 -0
  182. package/dist/templates/frontend-nextjs/public/placeholder.svg +1 -0
  183. package/dist/templates/frontend-nextjs/styles/globals.css +125 -0
  184. package/dist/templates/frontend-nextjs/tsconfig.json +27 -0
  185. package/dist/templates/module/.claude/agents/knex-table-implementer.md +113 -113
  186. package/dist/templates/module/.claude/agents/sundays-backend-builder.md +70 -70
  187. package/dist/templates/module/.claude/settings.local.json +10 -10
  188. package/dist/templates/module/CLAUDE.md +158 -158
  189. package/dist/templates/module/src/index.ts +9 -9
  190. package/dist/templates/module/tsconfig.json +19 -19
  191. package/package.json +40 -40
@@ -0,0 +1,558 @@
1
+ "use client"
2
+
3
+ import { useState, useRef, useEffect } from "react"
4
+ import { motion, AnimatePresence } from "framer-motion"
5
+ import {
6
+ Sparkles,
7
+ Send,
8
+ Check,
9
+ Circle,
10
+ Loader2,
11
+ Code2,
12
+ Rocket,
13
+ Globe,
14
+ FileCode,
15
+ Database,
16
+ Shield,
17
+ Layout,
18
+ Palette,
19
+ Copy,
20
+ ChevronRight
21
+ } from "lucide-react"
22
+ import { Button } from "@/components/ui/button"
23
+ import { cn } from "@/lib/utils"
24
+
25
+ type PipelineStage = "idle" | "design" | "scaffold" | "deploy" | "live"
26
+
27
+ interface Message {
28
+ id: string
29
+ type: "user" | "assistant"
30
+ content: string
31
+ stack?: StackItem[]
32
+ }
33
+
34
+ interface StackItem {
35
+ icon: React.ReactNode
36
+ label: string
37
+ description: string
38
+ }
39
+
40
+ interface BlueprintSpec {
41
+ title: string
42
+ items: string[]
43
+ }
44
+
45
+ const EXAMPLE_PROMPTS = [
46
+ "Sistema de facturación multi-tenant con auth JWT y generación de PDFs",
47
+ "E-commerce con carrito, pagos Stripe y panel admin",
48
+ "Dashboard de analytics con gráficos en tiempo real",
49
+ "App de gestión de tareas con colaboración en equipo"
50
+ ]
51
+
52
+ export function ProjectGenerator() {
53
+ const [messages, setMessages] = useState<Message[]>([])
54
+ const [inputValue, setInputValue] = useState("")
55
+ const [isGenerating, setIsGenerating] = useState(false)
56
+ const [pipelineStage, setPipelineStage] = useState<PipelineStage>("idle")
57
+ const [activeTab, setActiveTab] = useState<"blueprint" | "pipeline">("blueprint")
58
+ const [blueprint, setBlueprint] = useState<BlueprintSpec[] | null>(null)
59
+ const messagesEndRef = useRef<HTMLDivElement>(null)
60
+ const inputRef = useRef<HTMLTextAreaElement>(null)
61
+
62
+ const scrollToBottom = () => {
63
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
64
+ }
65
+
66
+ useEffect(() => {
67
+ scrollToBottom()
68
+ }, [messages])
69
+
70
+ const simulateGeneration = async (prompt: string) => {
71
+ setIsGenerating(true)
72
+
73
+ // Add user message
74
+ const userMessage: Message = {
75
+ id: Date.now().toString(),
76
+ type: "user",
77
+ content: prompt
78
+ }
79
+ setMessages(prev => [...prev, userMessage])
80
+
81
+ // Simulate AI thinking
82
+ await new Promise(resolve => setTimeout(resolve, 1500))
83
+
84
+ // Add assistant response with stack
85
+ const assistantMessage: Message = {
86
+ id: (Date.now() + 1).toString(),
87
+ type: "assistant",
88
+ content: "Perfecto. He analizado tu requerimiento y propongo el siguiente stack técnico:",
89
+ stack: [
90
+ { icon: <Database className="h-4 w-4" />, label: "Multi-tenant", description: "Arquitectura aislada por cliente" },
91
+ { icon: <Shield className="h-4 w-4" />, label: "Auth JWT", description: "Autenticación segura con tokens" },
92
+ { icon: <FileCode className="h-4 w-4" />, label: "CRUD completo", description: "Operaciones de datos estándar" },
93
+ { icon: <Layout className="h-4 w-4" />, label: "Dashboard", description: "Panel de administración" },
94
+ { icon: <Palette className="h-4 w-4" />, label: "UI Components", description: "Componentes shadcn/ui" },
95
+ ]
96
+ }
97
+ setMessages(prev => [...prev, assistantMessage])
98
+
99
+ // Generate blueprint
100
+ setBlueprint([
101
+ {
102
+ title: "Arquitectura",
103
+ items: ["Next.js 16 App Router", "TypeScript strict mode", "Tailwind CSS + shadcn/ui", "PostgreSQL + Prisma ORM"]
104
+ },
105
+ {
106
+ title: "Autenticación",
107
+ items: ["JWT con refresh tokens", "Middleware de protección", "Roles y permisos", "Sessions seguras"]
108
+ },
109
+ {
110
+ title: "Features",
111
+ items: ["CRUD dinámico", "Filtros y búsqueda", "Paginación optimizada", "Exportación de datos"]
112
+ },
113
+ {
114
+ title: "Infraestructura",
115
+ items: ["Vercel deployment", "Edge functions", "CDN global", "Monitoreo integrado"]
116
+ }
117
+ ])
118
+
119
+ setIsGenerating(false)
120
+ }
121
+
122
+ const handleGenerate = async () => {
123
+ if (pipelineStage !== "idle") return
124
+
125
+ setPipelineStage("design")
126
+ setActiveTab("pipeline")
127
+
128
+ // Simulate pipeline stages
129
+ await new Promise(resolve => setTimeout(resolve, 2000))
130
+ setPipelineStage("scaffold")
131
+ await new Promise(resolve => setTimeout(resolve, 2500))
132
+ setPipelineStage("deploy")
133
+ await new Promise(resolve => setTimeout(resolve, 2000))
134
+ setPipelineStage("live")
135
+ }
136
+
137
+ const handleSubmit = async (e: React.FormEvent) => {
138
+ e.preventDefault()
139
+ if (!inputValue.trim() || isGenerating) return
140
+
141
+ await simulateGeneration(inputValue)
142
+ setInputValue("")
143
+ }
144
+
145
+ const handleExampleClick = (prompt: string) => {
146
+ setInputValue(prompt)
147
+ inputRef.current?.focus()
148
+ }
149
+
150
+ const pipelineStages = [
151
+ { id: "design", label: "Design", icon: Palette, description: "Generando especificaciones" },
152
+ { id: "scaffold", label: "Scaffold", icon: Code2, description: "Creando estructura del proyecto" },
153
+ { id: "deploy", label: "Deploy", icon: Rocket, description: "Desplegando a producción" },
154
+ { id: "live", label: "Live", icon: Globe, description: "¡Tu proyecto está en vivo!" }
155
+ ]
156
+
157
+ const getStageStatus = (stageId: string) => {
158
+ const stageOrder = ["design", "scaffold", "deploy", "live"]
159
+ const currentIndex = stageOrder.indexOf(pipelineStage)
160
+ const stageIndex = stageOrder.indexOf(stageId)
161
+
162
+ if (pipelineStage === "idle") return "pending"
163
+ if (stageIndex < currentIndex) return "completed"
164
+ if (stageIndex === currentIndex) return "active"
165
+ return "pending"
166
+ }
167
+
168
+ return (
169
+ <div className="h-screen w-full bg-background flex">
170
+ {/* Main Chat Area */}
171
+ <div className="flex-1 flex flex-col min-w-0">
172
+ {/* Header */}
173
+ <header className="flex items-center gap-3 px-6 py-4 border-b border-border/50">
174
+ <div className="flex items-center justify-center w-9 h-9 rounded-lg bg-primary/10">
175
+ <Sparkles className="h-5 w-5 text-primary" />
176
+ </div>
177
+ <div>
178
+ <h1 className="text-lg font-semibold text-foreground">Project Generator</h1>
179
+ <p className="text-xs text-muted-foreground">Describe tu proyecto y genera el scaffolding completo</p>
180
+ </div>
181
+ </header>
182
+
183
+ {/* Messages Area */}
184
+ <div className="flex-1 overflow-y-auto px-6 py-6">
185
+ {messages.length === 0 ? (
186
+ <div className="h-full flex flex-col items-center justify-center">
187
+ <motion.div
188
+ initial={{ opacity: 0, y: 20 }}
189
+ animate={{ opacity: 1, y: 0 }}
190
+ className="text-center max-w-lg"
191
+ >
192
+ <div className="w-16 h-16 rounded-2xl bg-primary/10 flex items-center justify-center mx-auto mb-6">
193
+ <Sparkles className="h-8 w-8 text-primary" />
194
+ </div>
195
+ <h2 className="text-2xl font-semibold text-foreground mb-3">
196
+ ¿Qué quieres construir hoy?
197
+ </h2>
198
+ <p className="text-muted-foreground mb-8">
199
+ Describe tu proyecto y generaré la especificación técnica completa con el stack óptimo para tu caso de uso.
200
+ </p>
201
+
202
+ <div className="grid gap-2">
203
+ <p className="text-xs text-muted-foreground uppercase tracking-wider mb-2">Ejemplos</p>
204
+ {EXAMPLE_PROMPTS.map((prompt, index) => (
205
+ <motion.button
206
+ key={index}
207
+ initial={{ opacity: 0, x: -20 }}
208
+ animate={{ opacity: 1, x: 0 }}
209
+ transition={{ delay: index * 0.1 }}
210
+ onClick={() => handleExampleClick(prompt)}
211
+ className="group flex items-center gap-3 px-4 py-3 rounded-xl bg-card hover:bg-secondary/80 border border-border/50 hover:border-primary/30 transition-all text-left"
212
+ >
213
+ <ChevronRight className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" />
214
+ <span className="text-sm text-foreground/80 group-hover:text-foreground transition-colors">{prompt}</span>
215
+ </motion.button>
216
+ ))}
217
+ </div>
218
+ </motion.div>
219
+ </div>
220
+ ) : (
221
+ <div className="max-w-3xl mx-auto space-y-6">
222
+ <AnimatePresence mode="popLayout">
223
+ {messages.map((message) => (
224
+ <motion.div
225
+ key={message.id}
226
+ initial={{ opacity: 0, y: 20 }}
227
+ animate={{ opacity: 1, y: 0 }}
228
+ exit={{ opacity: 0, y: -20 }}
229
+ className={cn(
230
+ "flex gap-4",
231
+ message.type === "user" && "justify-end"
232
+ )}
233
+ >
234
+ {message.type === "assistant" && (
235
+ <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
236
+ <Sparkles className="h-4 w-4 text-primary" />
237
+ </div>
238
+ )}
239
+ <div className={cn(
240
+ "max-w-[80%]",
241
+ message.type === "user" && "bg-primary text-primary-foreground px-4 py-3 rounded-2xl rounded-tr-md"
242
+ )}>
243
+ <p className={cn(
244
+ "text-sm leading-relaxed",
245
+ message.type === "assistant" && "text-foreground"
246
+ )}>
247
+ {message.content}
248
+ </p>
249
+
250
+ {message.stack && (
251
+ <div className="mt-4 space-y-2">
252
+ <p className="text-xs text-muted-foreground uppercase tracking-wider mb-3">Stack confirmado</p>
253
+ <div className="grid gap-2">
254
+ {message.stack.map((item, index) => (
255
+ <motion.div
256
+ key={index}
257
+ initial={{ opacity: 0, x: -10 }}
258
+ animate={{ opacity: 1, x: 0 }}
259
+ transition={{ delay: index * 0.1 }}
260
+ className="flex items-center gap-3 px-3 py-2 rounded-lg bg-card border border-border/50"
261
+ >
262
+ <div className="w-7 h-7 rounded-md bg-primary/10 flex items-center justify-center text-primary">
263
+ {item.icon}
264
+ </div>
265
+ <div>
266
+ <p className="text-sm font-medium text-foreground">{item.label}</p>
267
+ <p className="text-xs text-muted-foreground">{item.description}</p>
268
+ </div>
269
+ </motion.div>
270
+ ))}
271
+ </div>
272
+ </div>
273
+ )}
274
+ </div>
275
+ </motion.div>
276
+ ))}
277
+ </AnimatePresence>
278
+
279
+ {isGenerating && (
280
+ <motion.div
281
+ initial={{ opacity: 0 }}
282
+ animate={{ opacity: 1 }}
283
+ className="flex gap-4"
284
+ >
285
+ <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
286
+ <Sparkles className="h-4 w-4 text-primary" />
287
+ </div>
288
+ <div className="flex items-center gap-2 text-muted-foreground">
289
+ <Loader2 className="h-4 w-4 animate-spin" />
290
+ <span className="text-sm">Analizando requerimientos...</span>
291
+ </div>
292
+ </motion.div>
293
+ )}
294
+
295
+ <div ref={messagesEndRef} />
296
+ </div>
297
+ )}
298
+ </div>
299
+
300
+ {/* Generate Button (when stack is confirmed) */}
301
+ {messages.length > 0 && !isGenerating && pipelineStage === "idle" && (
302
+ <motion.div
303
+ initial={{ opacity: 0, y: 20 }}
304
+ animate={{ opacity: 1, y: 0 }}
305
+ className="px-6 pb-4 flex justify-center"
306
+ >
307
+ <Button
308
+ onClick={handleGenerate}
309
+ size="lg"
310
+ className="gap-2 px-8 bg-primary hover:bg-primary/90 text-primary-foreground shadow-lg shadow-primary/25"
311
+ >
312
+ <Rocket className="h-4 w-4" />
313
+ Generar Proyecto
314
+ </Button>
315
+ </motion.div>
316
+ )}
317
+
318
+ {/* Input Area */}
319
+ <div className="border-t border-border/50 bg-card/30 backdrop-blur-sm px-6 py-4">
320
+ <form onSubmit={handleSubmit} className="max-w-3xl mx-auto">
321
+ <div className="relative flex items-end gap-3">
322
+ <div className="flex-1 relative">
323
+ <textarea
324
+ ref={inputRef}
325
+ value={inputValue}
326
+ onChange={(e) => setInputValue(e.target.value)}
327
+ onKeyDown={(e) => {
328
+ if (e.key === "Enter" && !e.shiftKey) {
329
+ e.preventDefault()
330
+ handleSubmit(e)
331
+ }
332
+ }}
333
+ placeholder="Describe tu proyecto..."
334
+ rows={1}
335
+ className="w-full resize-none rounded-xl bg-input border border-border/50 focus:border-primary/50 focus:ring-1 focus:ring-primary/30 px-4 py-3 pr-12 text-sm text-foreground placeholder:text-muted-foreground outline-none transition-all"
336
+ style={{ minHeight: "48px", maxHeight: "150px" }}
337
+ />
338
+ <div className="absolute right-2 bottom-2">
339
+ <Button
340
+ type="submit"
341
+ size="icon"
342
+ disabled={!inputValue.trim() || isGenerating}
343
+ className="h-8 w-8 rounded-lg bg-primary hover:bg-primary/90 disabled:opacity-50"
344
+ >
345
+ <Send className="h-4 w-4" />
346
+ </Button>
347
+ </div>
348
+ </div>
349
+ </div>
350
+ <p className="text-xs text-muted-foreground text-center mt-2">
351
+ Enter para enviar · Shift+Enter para nueva línea
352
+ </p>
353
+ </form>
354
+ </div>
355
+ </div>
356
+
357
+ {/* Preview Panel */}
358
+ <div className="w-[380px] border-l border-border/50 bg-card/30 flex flex-col">
359
+ {/* Panel Tabs */}
360
+ <div className="flex border-b border-border/50">
361
+ <button
362
+ onClick={() => setActiveTab("blueprint")}
363
+ className={cn(
364
+ "flex-1 px-4 py-3 text-sm font-medium transition-colors relative",
365
+ activeTab === "blueprint"
366
+ ? "text-foreground"
367
+ : "text-muted-foreground hover:text-foreground"
368
+ )}
369
+ >
370
+ Blueprint
371
+ {activeTab === "blueprint" && (
372
+ <motion.div
373
+ layoutId="activeTab"
374
+ className="absolute bottom-0 left-0 right-0 h-0.5 bg-primary"
375
+ />
376
+ )}
377
+ </button>
378
+ <button
379
+ onClick={() => setActiveTab("pipeline")}
380
+ className={cn(
381
+ "flex-1 px-4 py-3 text-sm font-medium transition-colors relative",
382
+ activeTab === "pipeline"
383
+ ? "text-foreground"
384
+ : "text-muted-foreground hover:text-foreground"
385
+ )}
386
+ >
387
+ Pipeline
388
+ {activeTab === "pipeline" && (
389
+ <motion.div
390
+ layoutId="activeTab"
391
+ className="absolute bottom-0 left-0 right-0 h-0.5 bg-primary"
392
+ />
393
+ )}
394
+ </button>
395
+ </div>
396
+
397
+ {/* Panel Content */}
398
+ <div className="flex-1 overflow-y-auto p-5">
399
+ <AnimatePresence mode="wait">
400
+ {activeTab === "blueprint" ? (
401
+ <motion.div
402
+ key="blueprint"
403
+ initial={{ opacity: 0, x: 20 }}
404
+ animate={{ opacity: 1, x: 0 }}
405
+ exit={{ opacity: 0, x: -20 }}
406
+ className="space-y-4"
407
+ >
408
+ {blueprint ? (
409
+ <>
410
+ <div className="flex items-center justify-between">
411
+ <h3 className="text-sm font-semibold text-foreground">Especificación Técnica</h3>
412
+ <Button variant="ghost" size="sm" className="h-7 gap-1.5 text-xs">
413
+ <Copy className="h-3 w-3" />
414
+ Copiar
415
+ </Button>
416
+ </div>
417
+
418
+ {blueprint.map((section, index) => (
419
+ <motion.div
420
+ key={index}
421
+ initial={{ opacity: 0, y: 10 }}
422
+ animate={{ opacity: 1, y: 0 }}
423
+ transition={{ delay: index * 0.1 }}
424
+ className="rounded-xl bg-secondary/50 border border-border/50 p-4"
425
+ >
426
+ <h4 className="text-xs font-semibold text-primary uppercase tracking-wider mb-3">
427
+ {section.title}
428
+ </h4>
429
+ <ul className="space-y-2">
430
+ {section.items.map((item, itemIndex) => (
431
+ <li key={itemIndex} className="flex items-center gap-2 text-sm text-foreground/80">
432
+ <div className="w-1.5 h-1.5 rounded-full bg-primary/60" />
433
+ {item}
434
+ </li>
435
+ ))}
436
+ </ul>
437
+ </motion.div>
438
+ ))}
439
+ </>
440
+ ) : (
441
+ <div className="h-full flex flex-col items-center justify-center py-16 text-center">
442
+ <div className="w-12 h-12 rounded-xl bg-secondary/50 flex items-center justify-center mb-4">
443
+ <FileCode className="h-6 w-6 text-muted-foreground" />
444
+ </div>
445
+ <p className="text-sm text-muted-foreground">
446
+ Sin blueprint todavía
447
+ </p>
448
+ <p className="text-xs text-muted-foreground/60 mt-1">
449
+ Describe tu proyecto para generar las especificaciones
450
+ </p>
451
+ </div>
452
+ )}
453
+ </motion.div>
454
+ ) : (
455
+ <motion.div
456
+ key="pipeline"
457
+ initial={{ opacity: 0, x: 20 }}
458
+ animate={{ opacity: 1, x: 0 }}
459
+ exit={{ opacity: 0, x: -20 }}
460
+ className="space-y-1"
461
+ >
462
+ {pipelineStages.map((stage, index) => {
463
+ const status = getStageStatus(stage.id)
464
+ const Icon = stage.icon
465
+
466
+ return (
467
+ <motion.div
468
+ key={stage.id}
469
+ initial={{ opacity: 0, x: 20 }}
470
+ animate={{ opacity: 1, x: 0 }}
471
+ transition={{ delay: index * 0.1 }}
472
+ className={cn(
473
+ "flex items-center gap-4 p-4 rounded-xl transition-all",
474
+ status === "active" && "bg-primary/10 border border-primary/30",
475
+ status === "completed" && "opacity-70"
476
+ )}
477
+ >
478
+ <div className={cn(
479
+ "w-10 h-10 rounded-xl flex items-center justify-center transition-colors",
480
+ status === "active" && "bg-primary text-primary-foreground",
481
+ status === "completed" && "bg-primary/20 text-primary",
482
+ status === "pending" && "bg-secondary text-muted-foreground"
483
+ )}>
484
+ {status === "completed" ? (
485
+ <Check className="h-5 w-5" />
486
+ ) : status === "active" ? (
487
+ <Loader2 className="h-5 w-5 animate-spin" />
488
+ ) : (
489
+ <Icon className="h-5 w-5" />
490
+ )}
491
+ </div>
492
+ <div className="flex-1 min-w-0">
493
+ <p className={cn(
494
+ "text-sm font-medium",
495
+ status === "active" ? "text-foreground" : "text-muted-foreground"
496
+ )}>
497
+ {stage.label}
498
+ </p>
499
+ {status === "active" && (
500
+ <motion.p
501
+ initial={{ opacity: 0 }}
502
+ animate={{ opacity: 1 }}
503
+ className="text-xs text-muted-foreground mt-0.5"
504
+ >
505
+ {stage.description}
506
+ </motion.p>
507
+ )}
508
+ </div>
509
+ {status === "completed" && (
510
+ <div className="w-2 h-2 rounded-full bg-primary" />
511
+ )}
512
+ </motion.div>
513
+ )
514
+ })}
515
+
516
+ {pipelineStage === "idle" && (
517
+ <div className="h-full flex flex-col items-center justify-center py-16 text-center">
518
+ <div className="w-12 h-12 rounded-xl bg-secondary/50 flex items-center justify-center mb-4">
519
+ <Rocket className="h-6 w-6 text-muted-foreground" />
520
+ </div>
521
+ <p className="text-sm text-muted-foreground">
522
+ Pipeline inactivo
523
+ </p>
524
+ <p className="text-xs text-muted-foreground/60 mt-1">
525
+ Genera un proyecto para ver el progreso aquí
526
+ </p>
527
+ </div>
528
+ )}
529
+
530
+ {pipelineStage === "live" && (
531
+ <motion.div
532
+ initial={{ opacity: 0, scale: 0.95 }}
533
+ animate={{ opacity: 1, scale: 1 }}
534
+ className="mt-6 p-4 rounded-xl bg-primary/10 border border-primary/30"
535
+ >
536
+ <div className="flex items-center gap-3 mb-3">
537
+ <div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center">
538
+ <Check className="h-4 w-4 text-primary-foreground" />
539
+ </div>
540
+ <div>
541
+ <p className="text-sm font-semibold text-foreground">¡Proyecto generado!</p>
542
+ <p className="text-xs text-muted-foreground">Listo para descargar</p>
543
+ </div>
544
+ </div>
545
+ <Button className="w-full gap-2 bg-primary hover:bg-primary/90">
546
+ <Globe className="h-4 w-4" />
547
+ Ver Proyecto en Vivo
548
+ </Button>
549
+ </motion.div>
550
+ )}
551
+ </motion.div>
552
+ )}
553
+ </AnimatePresence>
554
+ </div>
555
+ </div>
556
+ </div>
557
+ )
558
+ }
@@ -0,0 +1,11 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import {
5
+ ThemeProvider as NextThemesProvider,
6
+ type ThemeProviderProps,
7
+ } from 'next-themes'
8
+
9
+ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
10
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>
11
+ }
@@ -0,0 +1,66 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as AccordionPrimitive from '@radix-ui/react-accordion'
5
+ import { ChevronDownIcon } from 'lucide-react'
6
+
7
+ import { cn } from '@/lib/utils'
8
+
9
+ function Accordion({
10
+ ...props
11
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
12
+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />
13
+ }
14
+
15
+ function AccordionItem({
16
+ className,
17
+ ...props
18
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
19
+ return (
20
+ <AccordionPrimitive.Item
21
+ data-slot="accordion-item"
22
+ className={cn('border-b last:border-b-0', className)}
23
+ {...props}
24
+ />
25
+ )
26
+ }
27
+
28
+ function AccordionTrigger({
29
+ className,
30
+ children,
31
+ ...props
32
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
33
+ return (
34
+ <AccordionPrimitive.Header className="flex">
35
+ <AccordionPrimitive.Trigger
36
+ data-slot="accordion-trigger"
37
+ className={cn(
38
+ 'focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180',
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
45
+ </AccordionPrimitive.Trigger>
46
+ </AccordionPrimitive.Header>
47
+ )
48
+ }
49
+
50
+ function AccordionContent({
51
+ className,
52
+ children,
53
+ ...props
54
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
55
+ return (
56
+ <AccordionPrimitive.Content
57
+ data-slot="accordion-content"
58
+ className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
59
+ {...props}
60
+ >
61
+ <div className={cn('pt-0 pb-4', className)}>{children}</div>
62
+ </AccordionPrimitive.Content>
63
+ )
64
+ }
65
+
66
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }