@icarusmx/creta 1.3.4 → 1.3.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 (38) hide show
  1. package/bin/creta.js +8 -1576
  2. package/codex-refactor.txt +13 -0
  3. package/lib/builders/LessonBuilder.js +228 -0
  4. package/lib/builders/MenuBuilder.js +154 -0
  5. package/lib/builders/ProjectBuilder.js +56 -0
  6. package/lib/cli/index.js +81 -0
  7. package/lib/commands/help.js +5 -0
  8. package/lib/constants/paths.js +9 -0
  9. package/lib/data/enunciados.js +44 -0
  10. package/lib/data/lessons/index.js +25 -0
  11. package/lib/data/lessons/lesson1-system-decomposition.js +312 -0
  12. package/lib/data/lessons/lesson2-object-requests.js +318 -0
  13. package/lib/data/lessons/lesson3-only-way.js +349 -0
  14. package/lib/data/lessons/lesson4-operation-signatures.js +332 -0
  15. package/lib/data/lessons/lesson5-interface-set.js +341 -0
  16. package/lib/data/lessons/lesson6-interface-design.js +407 -0
  17. package/lib/data/lessons/lesson7-object-definition.js +375 -0
  18. package/lib/data/lessons/sintaxis/terminal-basico.js +46 -0
  19. package/lib/data/menus.js +43 -0
  20. package/lib/data/messages.js +28 -0
  21. package/lib/executors/enunciados-executor.js +63 -0
  22. package/lib/executors/portfolio-executor.js +167 -0
  23. package/lib/executors/proyectos-executor.js +23 -0
  24. package/lib/executors/sintaxis-executor.js +7 -0
  25. package/lib/templates/LevelModifier.js +287 -0
  26. package/lib/utils/file-utils.js +18 -0
  27. package/lib/utils/input.js +15 -0
  28. package/lib/utils/output.js +4 -0
  29. package/package.json +4 -1
  30. package/refactor.txt +581 -0
  31. package/test/enunciados.test.js +72 -0
  32. package/lessons/lesson1-system-decomposition.js +0 -313
  33. package/lessons/lesson2-object-requests.js +0 -309
  34. package/lessons/lesson3-only-way.js +0 -324
  35. package/lessons/lesson4-operation-signatures.js +0 -319
  36. package/lessons/lesson5-interface-set.js +0 -326
  37. package/lessons/lesson6-interface-design.js +0 -391
  38. package/lessons/lesson7-object-definition.js +0 -300
package/bin/creta.js CHANGED
@@ -1,1582 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { createInterface } from 'readline'
4
- import { execSync } from 'child_process'
5
- import fs from 'fs'
6
- import path from 'path'
7
- import { fileURLToPath } from 'url'
8
- import { CretaCodeSession } from '../lib/session.js'
9
- import { PullRequestTutorial } from '../lib/pr-tutorial.js'
10
- import { Lesson1SystemDecomposition } from '../lessons/lesson1-system-decomposition.js'
11
- import { Lesson2ObjectRequests } from '../lessons/lesson2-object-requests.js'
12
- import { Lesson3OnlyWay } from '../lessons/lesson3-only-way.js'
13
- import { Lesson4OperationSignatures } from '../lessons/lesson4-operation-signatures.js'
14
- import { Lesson5InterfaceSet } from '../lessons/lesson5-interface-set.js'
15
- import { Lesson6InterfaceDesign } from '../lessons/lesson6-interface-design.js'
16
- import { Lesson7ObjectDefinition } from '../lessons/lesson7-object-definition.js'
3
+ import { WELCOME_BANNER } from '../lib/data/messages.js'
4
+ import { runCLI } from '../lib/cli/index.js'
17
5
 
18
- const ENUNCIADOS = [
19
- {
20
- id: 1,
21
- texto: "La parte difícil del diseño orientado a objetos es descomponer un sistema como un conjunto de objetos que interactúan entre sí.",
22
- nivel: "Fundacional",
23
- enfoque: "Descomposición de sistemas"
24
- },
25
- {
26
- id: 2,
27
- texto: "Los objetos interactúan entre sí a través de solicitudes.",
28
- nivel: "Interacción",
29
- enfoque: "Comunicación entre objetos"
30
- },
31
- {
32
- id: 3,
33
- texto: "Las solicitudes son la única forma de conseguir que un objeto lleve a cabo una operación.",
34
- nivel: "Operaciones",
35
- enfoque: "Mecanismo de ejecución"
36
- },
37
- {
38
- id: 4,
39
- texto: "Cada operación declarada por un objeto debe incluir:\na) nombre de la operación\nb) insumos necesarios para realizar la operación\nc) el valor que regresa tras ejecutar la operación\n\nEstos tres elementos constituyen la firma de operación.",
40
- nivel: "Firmas",
41
- enfoque: "Contratos de operación"
42
- },
43
- {
44
- id: 5,
45
- texto: "La interfaz de un objeto es el conjunto de todas sus firmas de operación.",
46
- nivel: "Interfaces",
47
- enfoque: "Definición de contratos"
48
- },
49
- {
50
- id: 6,
51
- texto: "El énfasis al diseñar los objetos debe estar en la definición de sus solicitudes e interfaces.",
52
- nivel: "Diseño",
53
- enfoque: "Metodología de desarrollo"
54
- },
55
- {
56
- id: 7,
57
- texto: "Un objeto es un conjunto de datos y procedimientos que operan sobre esos datos.",
58
- nivel: "Definición",
59
- enfoque: "Naturaleza del objeto"
60
- }
61
- ]
6
+ console.log(WELCOME_BANNER)
62
7
 
63
- const args = process.argv.slice(2)
64
- const command = args[0]
65
-
66
- // ASCII Logo
67
- console.log(`
68
- █████████████████████████
69
- █ █ █ █ █ █ █
70
- █ C █ R █ E █ T █ A █ █ █
71
- █ █ █ █ █ █ █
72
- █████████████████████████
73
-
74
- Bienvenido a la escuela de software de icarus.mx
75
- Salgamos de este laberinto 🏛️
76
- `)
77
-
78
- if (!command) {
79
- // Default behavior: show main menu
80
- await startMainMenu()
81
- process.exit(0)
82
- }
83
-
84
- if (command.startsWith('portafolio')) {
85
- const level = command === 'portafolio' ? 0 : parseInt(command.split('-')[1]) || 0
86
-
87
- // Check if we're in an existing Creta project
88
- if (level > 0 && isInCretaProject()) {
89
- await unstuckProject(level)
90
- } else {
91
- await createPortfolioProject(level)
92
- }
93
- } else if (command === 'enunciados') {
94
- // Start enunciados selector
95
- await startEnunciadosSelector()
96
- } else if (command === 'code') {
97
- // Start interactive coding session
98
- const session = new CretaCodeSession()
99
- await session.start()
100
- } else if (command === 'help' || command === 'ayuda') {
101
- // Show available commands
102
- showHelp()
103
- } else {
104
- console.log(`Comando no reconocido: ${command}`)
105
- console.log("Escribe 'creta help' para ver comandos disponibles")
8
+ try {
9
+ const exitCode = await runCLI(process.argv.slice(2))
10
+ process.exit(exitCode)
11
+ } catch (error) {
12
+ console.error('\n❌ Ocurrió un error inesperado:', error.message)
106
13
  process.exit(1)
107
14
  }
108
-
109
- async function createPortfolioProject(level) {
110
- const rl = createInterface({
111
- input: process.stdin,
112
- output: process.stdout
113
- })
114
-
115
- const askQuestion = (question) => {
116
- return new Promise((resolve) => {
117
- rl.question(question, resolve)
118
- })
119
- }
120
-
121
- try {
122
- const name = await askQuestion("¿Cuál es tu nombre? ")
123
- const projectName = `${name.toLowerCase().replace(/\s+/g, '-')}-portafolio`
124
-
125
- console.log(`\n🚀 Creando proyecto: ${projectName}`)
126
- console.log(`📚 Nivel: ${level === 0 ? 'Reto completo' : `Con ${getLevelDescription(level)} completado`}`)
127
-
128
- await createProjectFiles(projectName, name, level)
129
-
130
- console.log(`\n✨ ¡Proyecto creado exitosamente!`)
131
- console.log(`\n📁 Ingresa a tu proyecto: cd ${projectName}`)
132
- console.log(`📦 Instala dependencias: npm install`)
133
- console.log(`🚀 Inicia el servidor: npm run dev`)
134
- console.log(`\n💡 Lee los comentarios en los archivos para saber qué hacer`)
135
-
136
- rl.close()
137
- } catch (error) {
138
- console.error('Error:', error.message)
139
- rl.close()
140
- process.exit(1)
141
- }
142
- }
143
-
144
- async function createProjectFiles(projectName, studentName, level) {
145
- const __filename = fileURLToPath(import.meta.url)
146
- const __dirname = path.dirname(__filename)
147
- const templatePath = path.join(__dirname, '../templates/sveltekit-portfolio')
148
- const targetPath = path.join(process.cwd(), projectName)
149
-
150
- // Check if template exists
151
- if (!fs.existsSync(templatePath)) {
152
- console.error(`Debug info:`)
153
- console.error(` - CLI location: ${__filename}`)
154
- console.error(` - Template path: ${templatePath}`)
155
- console.error(` - Template exists: ${fs.existsSync(templatePath)}`)
156
- throw new Error('Template no encontrado. Es posible que necesites reinstalar el paquete: npm uninstall -g @icarusmx/creta && npm install -g @icarusmx/creta')
157
- }
158
-
159
- // Create target directory
160
- if (fs.existsSync(targetPath)) {
161
- throw new Error(`El directorio ${projectName} ya existe`)
162
- }
163
-
164
- fs.mkdirSync(targetPath, { recursive: true })
165
-
166
- // Copy template files
167
- await copyTemplate(templatePath, targetPath, projectName, studentName, level)
168
- }
169
-
170
- async function copyTemplate(src, dest, projectName, studentName, level) {
171
- const items = fs.readdirSync(src)
172
-
173
- for (const item of items) {
174
- const srcPath = path.join(src, item)
175
- const destPath = path.join(dest, item)
176
-
177
- if (fs.statSync(srcPath).isDirectory()) {
178
- fs.mkdirSync(destPath, { recursive: true })
179
- await copyTemplate(srcPath, destPath, projectName, studentName, level)
180
- } else {
181
- // Check if this is a binary file
182
- const binaryExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.eot']
183
- const ext = path.extname(srcPath).toLowerCase()
184
-
185
- if (binaryExtensions.includes(ext)) {
186
- // Copy binary files directly without text processing
187
- fs.copyFileSync(srcPath, destPath)
188
- } else {
189
- // Process text files with placeholders
190
- let content = fs.readFileSync(srcPath, 'utf8')
191
-
192
- // Apply level-specific modifications first
193
- if (level > 0) {
194
- content = applyLevelModifications(content, item, level)
195
- }
196
-
197
- // Then replace placeholders
198
- content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName)
199
- content = content.replace(/\{\{STUDENT_NAME\}\}/g, studentName)
200
-
201
- fs.writeFileSync(destPath, content)
202
- }
203
- }
204
- }
205
- }
206
-
207
- function applyLevelModifications(content, filename, level) {
208
- if (filename === '+layout.svelte') {
209
- return applyLayoutModifications(content, level)
210
- }
211
-
212
- if (filename === '+page.svelte') {
213
- return applyPageModifications(content, level)
214
- }
215
-
216
- return content
217
- }
218
-
219
- function applyLayoutModifications(content, level) {
220
- if (level >= 1) {
221
- // Replace navbar placeholder with complete solution
222
- content = content.replace(
223
- '<nav class="bg-white shadow-sm">\n <!-- ⚠️ COMPLETA AQUÍ LA NAVEGACIÓN -->\n</nav>',
224
- `<nav class="bg-white shadow-sm">
225
- <div class="max-w-7xl mx-auto px-4">
226
- <div class="flex justify-between items-center py-3">
227
- <div class="flex items-center space-x-3">
228
- <img src="https://icarus.mx/logo.png" alt="Logo" class="w-8 h-8">
229
- <span class="text-xl font-bold text-gray-900">{{STUDENT_NAME}}</span>
230
- </div>
231
- <div class="hidden md:flex space-x-6">
232
- <a href="#inicio" class="text-gray-600 hover:text-blue-600">Inicio</a>
233
- <a href="#sobre-mi" class="text-gray-600 hover:text-blue-600">Sobre mí</a>
234
- <a href="#proyectos" class="text-gray-600 hover:text-blue-600">Proyectos</a>
235
- <a href="#contacto" class="text-gray-600 hover:text-blue-600">Contacto</a>
236
- </div>
237
- <button class="md:hidden">
238
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
239
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
240
- </svg>
241
- </button>
242
- </div>
243
- </div>
244
- </nav>`
245
- )
246
- }
247
-
248
- if (level >= 3) {
249
- // Replace footer placeholder with complete solution
250
- content = content.replace(
251
- '<footer class="bg-gray-50 border-t">\n <!-- ⚠️ COMPLETA AQUÍ EL FOOTER -->\n</footer>',
252
- `<footer class="bg-gray-50 border-t">
253
- <div class="max-w-7xl mx-auto py-8 px-4">
254
- <div class="text-center text-gray-600 text-sm space-y-2">
255
- <p>© ${new Date().getFullYear()} {{STUDENT_NAME}}. Todos los derechos reservados.</p>
256
- <p>Hecho con ❤️ en <a href="https://creta.school" class="text-blue-600 hover:underline">Creta</a></p>
257
- </div>
258
- </div>
259
- </footer>`
260
- )
261
- }
262
-
263
- return content
264
- }
265
-
266
- function applyPageModifications(content, level) {
267
- if (level >= 2) {
268
- // Replace hero placeholder with complete solution
269
- content = content.replace(
270
- `<section class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
271
- <!-- ⚠️ COMPLETA AQUÍ LA SECCIÓN HERO -->
272
- <div class="max-w-4xl mx-auto px-4 text-center">
273
- <h1 class="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
274
- Gracias por seguir aquí
275
- </h1>
276
- <p class="text-lg text-gray-600 mb-8">
277
- Abre el proyecto usando <code>code .</code> y sigue las instrucciones. Si tienes dudas, apóyate en el equipo
278
- </p>
279
- </div>
280
- </section>`,
281
- `<section id="inicio" class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
282
- <div class="max-w-4xl mx-auto px-4 text-center">
283
- <div class="animate-fade-in">
284
- <h1 class="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
285
- Hola, soy {{STUDENT_NAME}}
286
- </h1>
287
- <p class="text-lg text-gray-600 mb-8 max-w-2xl mx-auto">
288
- Soy un desarrollador apasionado por crear productos digitales que impacten positivamente.
289
- Aprendo construyendo y siempre busco nuevos desafíos.
290
- </p>
291
- <div class="flex justify-center space-x-4">
292
- <a href="#proyectos" class="bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-lg transition-colors duration-200 font-medium">
293
- Ver proyectos
294
- </a>
295
- <a href="#contacto" class="border border-blue-600 text-blue-600 hover:bg-blue-50 px-8 py-3 rounded-lg transition-colors duration-200 font-medium">
296
- Contáctame
297
- </a>
298
- </div>
299
- </div>
300
- </div>
301
- </section>`
302
- )
303
- }
304
-
305
- if (level >= 3) {
306
- // Add complete sections for level 3
307
- content = content.replace(
308
- `<!-- ⚠️ AQUÍ VAN LAS DEMÁS SECCIONES (SOBRE MÍ, PROYECTOS, CONTACTO) -->
309
- <!-- Revisa los comentarios arriba para saber qué crear -->`,
310
- `<!-- Sobre mí -->
311
- <section id="sobre-mi" class="py-20 bg-white">
312
- <div class="max-w-4xl mx-auto px-4">
313
- <div class="text-center mb-16">
314
- <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Sobre mí</h2>
315
- <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
316
- </div>
317
-
318
- <div class="grid md:grid-cols-2 gap-12 items-center">
319
- <div>
320
- <div class="bg-gray-100 aspect-square rounded-lg flex items-center justify-center mb-6 md:mb-0">
321
- <span class="text-gray-500 text-lg">Tu foto aquí</span>
322
- </div>
323
- </div>
324
- <div>
325
- <p class="text-lg text-gray-600 mb-6 leading-relaxed">
326
- Soy un desarrollador apasionado por crear productos digitales que impacten positivamente.
327
- Aprendo construyendo y siempre busco nuevos desafíos que me permitan crecer profesionalmente.
328
- </p>
329
- <div class="grid grid-cols-2 gap-4">
330
- <div class="bg-blue-50 p-4 rounded-lg text-center">
331
- <div class="text-2xl font-bold text-blue-600">2+</div>
332
- <div class="text-sm text-gray-600">Años aprendiendo</div>
333
- </div>
334
- <div class="bg-green-50 p-4 rounded-lg text-center">
335
- <div class="text-2xl font-bold text-green-600">5+</div>
336
- <div class="text-sm text-gray-600">Proyectos</div>
337
- </div>
338
- </div>
339
- </div>
340
- </div>
341
- </div>
342
- </section>
343
-
344
- <!-- Proyectos -->
345
- <section id="proyectos" class="py-20 bg-gray-50">
346
- <div class="max-w-6xl mx-auto px-4">
347
- <div class="text-center mb-16">
348
- <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Mis Proyectos</h2>
349
- <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
350
- <p class="text-lg text-gray-600 max-w-2xl mx-auto">
351
- Algunos de los proyectos en los que he trabajado y que demuestran mis habilidades
352
- </p>
353
- </div>
354
-
355
- <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
356
- <!-- Proyecto 1 -->
357
- <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
358
- <div class="bg-gradient-to-br from-blue-400 to-blue-600 h-48 flex items-center justify-center">
359
- <span class="text-white text-lg font-medium">Proyecto 1</span>
360
- </div>
361
- <div class="p-6">
362
- <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
363
- <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
364
- <div class="flex space-x-3">
365
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
366
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
367
- </div>
368
- </div>
369
- </div>
370
-
371
- <!-- Proyecto 2 -->
372
- <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
373
- <div class="bg-gradient-to-br from-green-400 to-green-600 h-48 flex items-center justify-center">
374
- <span class="text-white text-lg font-medium">Proyecto 2</span>
375
- </div>
376
- <div class="p-6">
377
- <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
378
- <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
379
- <div class="flex space-x-3">
380
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
381
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
382
- </div>
383
- </div>
384
- </div>
385
-
386
- <!-- Proyecto 3 -->
387
- <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
388
- <div class="bg-gradient-to-br from-purple-400 to-purple-600 h-48 flex items-center justify-center">
389
- <span class="text-white text-lg font-medium">Proyecto 3</span>
390
- </div>
391
- <div class="p-6">
392
- <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
393
- <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
394
- <div class="flex space-x-3">
395
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
396
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
397
- </div>
398
- </div>
399
- </div>
400
- </div>
401
- </div>
402
- </section>
403
-
404
- <!-- Contacto -->
405
- <section id="contacto" class="py-20 bg-white">
406
- <div class="max-w-4xl mx-auto px-4">
407
- <div class="text-center mb-16">
408
- <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Contacto</h2>
409
- <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
410
- <p class="text-lg text-gray-600 max-w-2xl mx-auto">
411
- ¿Tienes un proyecto en mente? ¡Hablemos y veamos cómo puedo ayudarte!
412
- </p>
413
- </div>
414
-
415
- <div class="grid md:grid-cols-2 gap-12">
416
- <div>
417
- <h3 class="text-xl font-semibold text-gray-900 mb-6">Información de contacto</h3>
418
- <div class="space-y-4">
419
- <div class="flex items-center space-x-3">
420
- <div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
421
- <svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
422
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
423
- </svg>
424
- </div>
425
- <div>
426
- <p class="text-gray-900 font-medium">Email</p>
427
- <p class="text-gray-600">tu.email@ejemplo.com</p>
428
- </div>
429
- </div>
430
-
431
- <div class="flex items-center space-x-3">
432
- <div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
433
- <svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
434
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
435
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
436
- </svg>
437
- </div>
438
- <div>
439
- <p class="text-gray-900 font-medium">Ubicación</p>
440
- <p class="text-gray-600">Tu ciudad, País</p>
441
- </div>
442
- </div>
443
- </div>
444
-
445
- <div class="mt-8">
446
- <h4 class="text-lg font-medium text-gray-900 mb-4">Sígueme</h4>
447
- <div class="flex space-x-4">
448
- <a href="#" class="w-10 h-10 bg-gray-100 hover:bg-blue-100 rounded-lg flex items-center justify-center transition-colors duration-200">
449
- <span class="text-gray-600 hover:text-blue-600">LI</span>
450
- </a>
451
- <a href="#" class="w-10 h-10 bg-gray-100 hover:bg-blue-100 rounded-lg flex items-center justify-center transition-colors duration-200">
452
- <span class="text-gray-600 hover:text-blue-600">GH</span>
453
- </a>
454
- <a href="#" class="w-10 h-10 bg-gray-100 hover:bg-blue-100 rounded-lg flex items-center justify-center transition-colors duration-200">
455
- <span class="text-gray-600 hover:text-blue-600">TW</span>
456
- </a>
457
- </div>
458
- </div>
459
- </div>
460
-
461
- <div>
462
- <form class="space-y-6">
463
- <div>
464
- <label for="name" class="block text-sm font-medium text-gray-700 mb-2">Nombre</label>
465
- <input type="text" id="name" name="name" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent">
466
- </div>
467
- <div>
468
- <label for="email" class="block text-sm font-medium text-gray-700 mb-2">Email</label>
469
- <input type="email" id="email" name="email" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent">
470
- </div>
471
- <div>
472
- <label for="message" class="block text-sm font-medium text-gray-700 mb-2">Mensaje</label>
473
- <textarea id="message" name="message" rows="4" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-600 focus:border-transparent"></textarea>
474
- </div>
475
- <button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-lg transition-colors duration-200 font-medium">
476
- Enviar mensaje
477
- </button>
478
- </form>
479
- </div>
480
- </div>
481
- </div>
482
- </section>`
483
- )
484
- }
485
-
486
- return content
487
- }
488
-
489
- function getLevelDescription(level) {
490
- const descriptions = {
491
- 1: 'navbar',
492
- 2: 'navbar + hero',
493
- 3: 'todo el portafolio'
494
- }
495
- return descriptions[level] || 'nivel desconocido'
496
- }
497
-
498
- function isInCretaProject() {
499
- // Check if we're in a Creta project by looking for key files
500
- let currentDir
501
- try {
502
- currentDir = process.cwd()
503
- } catch (error) {
504
- // If we can't get the current directory, we're definitely not in a Creta project
505
- console.error('Error: No se puede acceder al directorio actual. Asegúrate de estar en un directorio válido.')
506
- return false
507
- }
508
-
509
- const packageJsonPath = path.join(currentDir, 'package.json')
510
- const layoutPath = path.join(currentDir, 'src/routes/+layout.svelte')
511
- const pagePath = path.join(currentDir, 'src/routes/+page.svelte')
512
-
513
- // Must have package.json and SvelteKit structure
514
- if (!fs.existsSync(packageJsonPath) || !fs.existsSync(layoutPath) || !fs.existsSync(pagePath)) {
515
- return false
516
- }
517
-
518
- // Check if layout contains Creta comments
519
- try {
520
- const layoutContent = fs.readFileSync(layoutPath, 'utf8')
521
- return layoutContent.includes('RETO CRETA') || layoutContent.includes('🧭 RETO 1: NAVBAR')
522
- } catch (error) {
523
- return false
524
- }
525
- }
526
-
527
- async function unstuckProject(level) {
528
- console.log(`\n🔓 ¡Te ayudo a desbloquear el nivel ${level}!`)
529
- console.log(`📚 Agregando código para: ${getLevelDescription(level)}`)
530
-
531
- try {
532
- let currentDir
533
- try {
534
- currentDir = process.cwd()
535
- } catch (error) {
536
- console.error('Error: No se puede acceder al directorio actual. Asegúrate de estar en un directorio válido.')
537
- process.exit(1)
538
- }
539
- const layoutPath = path.join(currentDir, 'src/routes/+layout.svelte')
540
- const pagePath = path.join(currentDir, 'src/routes/+page.svelte')
541
-
542
- // Get student name from package.json title if possible
543
- let studentName = 'Tu nombre'
544
- try {
545
- const packageJsonPath = path.join(currentDir, 'package.json')
546
- const packageContent = fs.readFileSync(packageJsonPath, 'utf8')
547
- const packageJson = JSON.parse(packageContent)
548
- if (packageJson.name && packageJson.name.includes('-portafolio')) {
549
- studentName = packageJson.name.replace('-portafolio', '').split('-').map(word =>
550
- word.charAt(0).toUpperCase() + word.slice(1)
551
- ).join(' ')
552
- }
553
- } catch (error) {
554
- // Use default if can't parse
555
- }
556
-
557
- // Apply modifications to existing files
558
- if (fs.existsSync(layoutPath)) {
559
- let layoutContent = fs.readFileSync(layoutPath, 'utf8')
560
- layoutContent = applyLayoutModifications(layoutContent, level)
561
- // Replace placeholders in layout
562
- layoutContent = layoutContent.replace(/\{\{STUDENT_NAME\}\}/g, studentName)
563
- layoutContent = layoutContent.replace(/\{\{PROJECT_NAME\}\}/g, `${studentName.toLowerCase().replace(/\s+/g, '-')}-portafolio`)
564
- fs.writeFileSync(layoutPath, layoutContent)
565
- console.log(`✅ Actualizado: src/routes/+layout.svelte`)
566
- }
567
-
568
- if (fs.existsSync(pagePath)) {
569
- let pageContent = fs.readFileSync(pagePath, 'utf8')
570
- pageContent = applyPageModifications(pageContent, level)
571
- // Replace placeholders in page
572
- pageContent = pageContent.replace(/\{\{STUDENT_NAME\}\}/g, studentName)
573
- pageContent = pageContent.replace(/\{\{PROJECT_NAME\}\}/g, `${studentName.toLowerCase().replace(/\s+/g, '-')}-portafolio`)
574
- fs.writeFileSync(pagePath, pageContent)
575
- console.log(`✅ Actualizado: src/routes/+page.svelte`)
576
- }
577
-
578
- console.log(`\n🎉 ¡Listo! Ahora tienes el código del nivel ${level}`)
579
- console.log(`💡 Revisa los archivos actualizados para ver qué se agregó`)
580
- console.log(`🚀 Continúa con: npm run dev`)
581
-
582
- } catch (error) {
583
- console.error('Error al actualizar el proyecto:', error.message)
584
- process.exit(1)
585
- }
586
- }
587
-
588
- function showHelp() {
589
- console.log("\n📚 Comandos disponibles:")
590
- console.log(" creta - Explora los 7 enunciados fundamentales (comando principal) 🧠")
591
- console.log(" creta enunciados - Explora los 7 enunciados fundamentales 🧠")
592
- console.log(" creta portafolio - Crea tu portafolio personal (reto completo)")
593
- console.log(" creta portafolio-1 - Desbloquea nivel 1 (navbar) 🔓")
594
- console.log(" creta portafolio-2 - Desbloquea nivel 2 (navbar + hero) 🔓")
595
- console.log(" creta portafolio-3 - Desbloquea nivel 3 (solución completa) 🔓")
596
- console.log(" creta code - Inicia sesión interactiva de programación 🤖")
597
- console.log(" creta help - Muestra esta ayuda")
598
- console.log("\n💡 Tip: Si estás dentro de un proyecto existente, los comandos")
599
- console.log(" portafolio-1/2/3 actualizarán tus archivos directamente")
600
- console.log("\n🎯 La filosofía Creta: partir de enunciados que generan 'ruido' para")
601
- console.log(" construir comprensión real, no solo sintaxis.")
602
- }
603
-
604
- async function startMainMenu() {
605
- // Always try interactive mode first, fall back only if it fails
606
- try {
607
- return await startMainMenuInteractive()
608
- } catch (error) {
609
- // If interactive mode fails, fall back to numbered selection
610
- console.log('\nModo interactivo no disponible, usando selección numérica...\n')
611
- return await startMainMenuFallback()
612
- }
613
- }
614
-
615
- async function startMainMenuInteractive() {
616
- // Check if setRawMode is available before trying to use it
617
- if (typeof process.stdin.setRawMode !== 'function') {
618
- throw new Error('setRawMode not available')
619
- }
620
-
621
- console.log("Te ofrecemos las siguientes opciones:")
622
-
623
- const options = [
624
- { id: 1, title: "Aprender conceptos", description: "Explora los enunciados fundamentales" },
625
- { id: 2, title: "Construir proyectos", description: "Practica con proyectos reales" }
626
- ]
627
-
628
- let selectedIndex = 0
629
-
630
- // Enable raw mode to capture arrow keys
631
- try {
632
- process.stdin.setRawMode(true)
633
- process.stdin.resume()
634
- process.stdin.setEncoding('utf8')
635
- } catch (error) {
636
- throw new Error('Failed to enable raw mode: ' + error.message)
637
- }
638
-
639
- const renderOptions = () => {
640
- // Clear previous output and move cursor to top
641
- process.stdout.write('\x1b[2J')
642
- process.stdout.write('\x1b[H')
643
-
644
- console.log(`
645
- █████████████████████████
646
- █ █ █ █ █ █ █
647
- █ C █ R █ E █ T █ A █ █ █
648
- █ █ █ █ █ █ █
649
- █████████████████████████
650
-
651
- Bienvenido a la escuela de software de icarus.mx
652
- Salgamos de este laberinto 🏛️
653
- `)
654
- console.log("Te ofrecemos las siguientes opciones:")
655
-
656
- options.forEach((option, index) => {
657
- const isSelected = index === selectedIndex
658
- const prefix = isSelected ? '▶ ' : ' '
659
- const highlight = isSelected ? '\x1b[36m' : '\x1b[37m' // cyan for selected, white for normal
660
- const reset = '\x1b[0m'
661
-
662
- console.log(`${highlight}${prefix}${option.title}${reset}`)
663
- if (isSelected) {
664
- console.log(`${highlight} ${option.description}${reset}`)
665
- }
666
- console.log("")
667
- })
668
- }
669
-
670
- return new Promise((resolve) => {
671
- renderOptions()
672
-
673
- const onKeyPress = (key) => {
674
- if (key === 'q' || key === '\x03') { // q or Ctrl+C
675
- process.stdin.setRawMode(false)
676
- process.stdin.removeListener('data', onKeyPress)
677
- console.log("\nHecho con <3 por icarus.mx")
678
- resolve()
679
- return
680
- }
681
-
682
- if (key === '\r' || key === '\n') { // Enter
683
- process.stdin.setRawMode(false)
684
- process.stdin.removeListener('data', onKeyPress)
685
-
686
- const selectedOption = options[selectedIndex]
687
-
688
- // Clear screen
689
- process.stdout.write('\x1b[2J')
690
- process.stdout.write('\x1b[H')
691
-
692
- if (selectedOption.id === 1) {
693
- startEnunciadosSelector().then(resolve)
694
- } else if (selectedOption.id === 2) {
695
- startProyectosSelector().then(resolve)
696
- }
697
- return
698
- }
699
-
700
- // Handle arrow keys (escape sequences)
701
- if (key === '\u001b[A') { // Up arrow
702
- selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : options.length - 1
703
- renderOptions()
704
- } else if (key === '\u001b[B') { // Down arrow
705
- selectedIndex = selectedIndex < options.length - 1 ? selectedIndex + 1 : 0
706
- renderOptions()
707
- }
708
- }
709
-
710
- process.stdin.on('data', onKeyPress)
711
- })
712
- }
713
-
714
- async function startMainMenuFallback() {
715
- const rl = createInterface({
716
- input: process.stdin,
717
- output: process.stdout
718
- })
719
-
720
- const askQuestion = (question) => {
721
- return new Promise((resolve) => {
722
- rl.question(question, resolve)
723
- })
724
- }
725
-
726
- try {
727
- console.log("Te ofrecemos las siguientes opciones:")
728
- console.log("")
729
- console.log("1. Aprender conceptos - Explora los enunciados fundamentales")
730
- console.log("2. Construir proyectos - Practica con proyectos reales")
731
- console.log("")
732
-
733
- const respuesta = await askQuestion("Elige una opción (1-2) o 'q' para salir: ")
734
-
735
- if (respuesta.toLowerCase() === 'q') {
736
- console.log("Hecho con <3 por icarus.mx")
737
- rl.close()
738
- return
739
- }
740
-
741
- const opcionSeleccionada = parseInt(respuesta)
742
-
743
- if (opcionSeleccionada === 1) {
744
- rl.close()
745
- await startEnunciadosSelector()
746
- } else if (opcionSeleccionada === 2) {
747
- rl.close()
748
- await startProyectosSelector()
749
- } else {
750
- console.log("❌ Opción no válida. Elige 1 o 2.")
751
- rl.close()
752
- }
753
-
754
- } catch (error) {
755
- console.error('Error:', error.message)
756
- rl.close()
757
- process.exit(1)
758
- }
759
- }
760
-
761
- async function startEnunciadosSelector() {
762
- // Always try interactive mode first, fall back only if it fails
763
- try {
764
- return await startEnunciadosSelectorInteractive()
765
- } catch (error) {
766
- // If interactive mode fails, fall back to numbered selection
767
- console.log('\nModo interactivo no disponible, usando selección numérica...\n')
768
- return await startEnunciadosSelectorFallback()
769
- }
770
- }
771
-
772
- async function startEnunciadosSelectorInteractive() {
773
- // Check if setRawMode is available before trying to use it
774
- if (typeof process.stdin.setRawMode !== 'function') {
775
- throw new Error('setRawMode not available')
776
- }
777
-
778
- console.log("Los enunciados fundamentales son un conjunto de afirmaciones que tienen el propósito de ejercitar el pensamiento orientado a objetos de los estudiantes de Creta.")
779
- console.log("")
780
- console.log("Escogimos estos enunciados porque creemos que exhaustan los conceptos mínimos necesarios para entender el paradigma de la programación orientada a objetos.")
781
-
782
- // Wait for user to press Enter before showing the list
783
- const rl = createInterface({
784
- input: process.stdin,
785
- output: process.stdout
786
- })
787
-
788
- await new Promise((resolve) => {
789
- rl.question("\nPresiona Enter para comenzar...", () => {
790
- rl.close()
791
- resolve()
792
- })
793
- })
794
-
795
- console.log("\n\n")
796
- console.log("Elige qué enunciado te gustaría explorar:")
797
- console.log("")
798
-
799
- let selectedIndex = 0
800
- let running = true
801
-
802
- // Enable raw mode to capture arrow keys
803
- try {
804
- process.stdin.setRawMode(true)
805
- process.stdin.resume()
806
- process.stdin.setEncoding('utf8')
807
- } catch (error) {
808
- throw new Error('Failed to enable raw mode: ' + error.message)
809
- }
810
-
811
- const renderOptions = () => {
812
- // Clear previous output and move cursor to top
813
- process.stdout.write('\x1b[2J')
814
- process.stdout.write('\x1b[H')
815
-
816
- console.log("Los enunciados fundamentales son un conjunto de afirmaciones que tienen el propósito de ejercitar el pensamiento orientado a objetos de los estudiantes de Creta.")
817
- console.log("")
818
- console.log("Escogimos estos enunciados porque creemos que exhaustan los conceptos mínimos necesarios para entender el paradigma de la programación orientada a objetos.")
819
- console.log("")
820
- console.log("Elige qué enunciado te gustaría explorar:")
821
- console.log("")
822
-
823
- ENUNCIADOS.forEach((enunciado, index) => {
824
- const isSelected = index === selectedIndex
825
- const prefix = isSelected ? '▶ ' : ' '
826
- const highlight = isSelected ? '\x1b[36m' : '\x1b[37m' // cyan for selected, white for normal
827
- const reset = '\x1b[0m'
828
-
829
- // Format the enunciado text with proper indentation for lists
830
- let formattedText = enunciado.texto
831
- if (enunciado.id === 4) {
832
- // Special formatting for enunciado 4 with list items
833
- formattedText = formattedText.replace(/\n([abc])\)/g, '\n $1)')
834
- formattedText = formattedText.replace(/\n\nEstos tres elementos/g, '\n\n Estos tres elementos')
835
- }
836
- console.log(`${highlight}${prefix}${index + 1}. ${formattedText}${reset}`)
837
- console.log("")
838
- })
839
- }
840
-
841
- return new Promise((resolve) => {
842
- renderOptions()
843
-
844
- const onKeyPress = async (key) => {
845
- if (key === 'q' || key === '\x03') { // q or Ctrl+C
846
- process.stdin.setRawMode(false)
847
- process.stdin.removeListener('data', onKeyPress)
848
- console.log("\nHecho con <3 por icarus.mx")
849
- resolve()
850
- return
851
- }
852
-
853
- if (key === '\r' || key === '\n') { // Enter
854
- process.stdin.setRawMode(false)
855
- process.stdin.removeListener('data', onKeyPress)
856
-
857
- const enunciadoSeleccionado = ENUNCIADOS[selectedIndex]
858
-
859
- // Clear screen and show selection
860
- process.stdout.write('\x1b[2J')
861
- process.stdout.write('\x1b[H')
862
-
863
- // Check if we have a lesson implementation for this enunciado
864
- if (enunciadoSeleccionado.id === 1) {
865
- console.log(`${enunciadoSeleccionado.texto}`)
866
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
867
-
868
- try {
869
- const lesson1 = new Lesson1SystemDecomposition()
870
- await lesson1.start()
871
- await returnToMainMenu()
872
- } catch (error) {
873
- console.error("\n❌ Error al ejecutar la lección:", error.message)
874
- console.log("\n🚀 Próximamente:")
875
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
876
- console.log("- Ejercicios prácticos que ilustren el concepto")
877
- console.log("- Proyectos específicos para internalizar la idea")
878
- await returnToMainMenu()
879
- }
880
- } else if (enunciadoSeleccionado.id === 1) {
881
- console.log(`${enunciadoSeleccionado.texto}`)
882
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
883
-
884
- try {
885
- const lesson1 = new Lesson1SystemDecomposition()
886
- await lesson1.start()
887
- await returnToMainMenu()
888
- } catch (error) {
889
- console.error("\n❌ Error al ejecutar la lección:", error.message)
890
- console.log("\n🚀 Próximamente:")
891
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
892
- console.log("- Ejercicios prácticos que ilustren el concepto")
893
- console.log("- Proyectos específicos para internalizar la idea")
894
- await returnToMainMenu()
895
- }
896
- } else if (enunciadoSeleccionado.id === 2) {
897
- console.log(`${enunciadoSeleccionado.texto}`)
898
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
899
-
900
- try {
901
- const lesson2 = new Lesson2ObjectRequests()
902
- await lesson2.start()
903
- await returnToMainMenu()
904
- } catch (error) {
905
- console.error("\n❌ Error al ejecutar la lección:", error.message)
906
- console.log("\n🚀 Próximamente:")
907
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
908
- console.log("- Ejercicios prácticos que ilustren el concepto")
909
- console.log("- Proyectos específicos para internalizar la idea")
910
- await returnToMainMenu()
911
- }
912
- } else if (enunciadoSeleccionado.id === 3) {
913
- console.log(`${enunciadoSeleccionado.texto}`)
914
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
915
-
916
- try {
917
- const lesson3 = new Lesson3OnlyWay()
918
- await lesson3.start()
919
- await returnToMainMenu()
920
- } catch (error) {
921
- console.error("\n❌ Error al ejecutar la lección:", error.message)
922
- console.log("\n🚀 Próximamente:")
923
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
924
- console.log("- Ejercicios prácticos que ilustren el concepto")
925
- console.log("- Proyectos específicos para internalizar la idea")
926
- await returnToMainMenu()
927
- }
928
- } else if (enunciadoSeleccionado.id === 4) {
929
- console.log(`${enunciadoSeleccionado.texto}`)
930
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
931
-
932
- try {
933
- const lesson4 = new Lesson4OperationSignatures()
934
- await lesson4.start()
935
- await returnToMainMenu()
936
- } catch (error) {
937
- console.error("\n❌ Error al ejecutar la lección:", error.message)
938
- console.log("\n🚀 Próximamente:")
939
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
940
- console.log("- Ejercicios prácticos que ilustren el concepto")
941
- console.log("- Proyectos específicos para internalizar la idea")
942
- await returnToMainMenu()
943
- }
944
- } else if (enunciadoSeleccionado.id === 5) {
945
- console.log(`${enunciadoSeleccionado.texto}`)
946
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
947
-
948
- try {
949
- const lesson5 = new Lesson5InterfaceSet()
950
- await lesson5.start()
951
- await returnToMainMenu()
952
- } catch (error) {
953
- console.error("\n❌ Error al ejecutar la lección:", error.message)
954
- console.log("\n🚀 Próximamente:")
955
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
956
- console.log("- Ejercicios prácticos que ilustren el concepto")
957
- console.log("- Proyectos específicos para internalizar la idea")
958
- await returnToMainMenu()
959
- }
960
- } else if (enunciadoSeleccionado.id === 6) {
961
- console.log(`${enunciadoSeleccionado.texto}`)
962
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
963
-
964
- try {
965
- const lesson6 = new Lesson6InterfaceDesign()
966
- await lesson6.start()
967
- await returnToMainMenu()
968
- } catch (error) {
969
- console.error("\n❌ Error al ejecutar la lección:", error.message)
970
- console.log("\n🚀 Próximamente:")
971
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
972
- console.log("- Ejercicios prácticos que ilustren el concepto")
973
- console.log("- Proyectos específicos para internalizar la idea")
974
- await returnToMainMenu()
975
- }
976
- } else if (enunciadoSeleccionado.id === 7) {
977
- console.log(`${enunciadoSeleccionado.texto}`)
978
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
979
-
980
- try {
981
- const lesson7 = new Lesson7ObjectDefinition()
982
- await lesson7.start()
983
- await returnToMainMenu()
984
- } catch (error) {
985
- console.error("\n❌ Error al ejecutar la lección:", error.message)
986
- console.log("\n🚀 Próximamente:")
987
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
988
- console.log("- Ejercicios prácticos que ilustren el concepto")
989
- console.log("- Proyectos específicos para internalizar la idea")
990
- await returnToMainMenu()
991
- }
992
- } else {
993
- console.log("\n🚀 Próximamente:")
994
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
995
- console.log("- Ejercicios prácticos que ilustren el concepto")
996
- console.log("- Proyectos específicos para internalizar la idea")
997
-
998
- console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
999
- console.log(" te genera más curiosidad o confusión?")
1000
- }
1001
-
1002
- resolve()
1003
- return
1004
- }
1005
-
1006
- // Handle arrow keys (escape sequences)
1007
- if (key === '\u001b[A') { // Up arrow
1008
- selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : ENUNCIADOS.length - 1
1009
- renderOptions()
1010
- } else if (key === '\u001b[B') { // Down arrow
1011
- selectedIndex = selectedIndex < ENUNCIADOS.length - 1 ? selectedIndex + 1 : 0
1012
- renderOptions()
1013
- }
1014
- }
1015
-
1016
- process.stdin.on('data', onKeyPress)
1017
- })
1018
- }
1019
-
1020
- async function startEnunciadosSelectorFallback() {
1021
- const rl = createInterface({
1022
- input: process.stdin,
1023
- output: process.stdout
1024
- })
1025
-
1026
- const askQuestion = (question) => {
1027
- return new Promise((resolve) => {
1028
- rl.question(question, resolve)
1029
- })
1030
- }
1031
-
1032
- try {
1033
- console.log("Los enunciados fundamentales son un conjunto de afirmaciones que tienen el propósito de ejercitar el pensamiento orientado a objetos de los estudiantes de Creta.")
1034
- console.log("")
1035
- console.log("Escogimos estos enunciados porque creemos que exhaustan los conceptos mínimos necesarios para entender el paradigma de la programación orientada a objetos.")
1036
-
1037
- await askQuestion("\nPresiona Enter para comenzar...")
1038
-
1039
- console.log("\n\n")
1040
- console.log("Elige qué enunciado te gustaría explorar:")
1041
- console.log("")
1042
-
1043
- ENUNCIADOS.forEach((enunciado, index) => {
1044
- // Format the enunciado text with proper indentation for lists
1045
- let formattedText = enunciado.texto
1046
- if (enunciado.id === 4) {
1047
- // Special formatting for enunciado 4 with list items
1048
- formattedText = formattedText.replace(/\n([abc])\)/g, '\n $1)')
1049
- formattedText = formattedText.replace(/\n\nEstos tres elementos/g, '\n\n Estos tres elementos')
1050
- }
1051
- console.log(`${index + 1}. ${formattedText}`)
1052
- console.log("")
1053
- })
1054
-
1055
- const respuesta = await askQuestion("Elige un número (1-7) o 'q' para salir: ")
1056
-
1057
- if (respuesta.toLowerCase() === 'q') {
1058
- console.log("Hecho con <3 por icarus.mx")
1059
- rl.close()
1060
- return
1061
- }
1062
-
1063
- const numeroSeleccionado = parseInt(respuesta)
1064
-
1065
- if (numeroSeleccionado >= 1 && numeroSeleccionado <= 7) {
1066
- const enunciadoSeleccionado = ENUNCIADOS[numeroSeleccionado - 1]
1067
-
1068
- // Check if we have a lesson implementation for this enunciado
1069
- if (enunciadoSeleccionado.id === 1) {
1070
- console.log(`${enunciadoSeleccionado.texto}`)
1071
- rl.close()
1072
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1073
-
1074
- try {
1075
- const lesson1 = new Lesson1SystemDecomposition()
1076
- await lesson1.start()
1077
- } catch (error) {
1078
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1079
- console.log("\n🚀 Próximamente:")
1080
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1081
- console.log("- Ejercicios prácticos que ilustren el concepto")
1082
- console.log("- Proyectos específicos para internalizar la idea")
1083
- }
1084
- return
1085
- } else if (enunciadoSeleccionado.id === 1) {
1086
- console.log(`${enunciadoSeleccionado.texto}`)
1087
- rl.close()
1088
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1089
-
1090
- try {
1091
- const lesson1 = new Lesson1SystemDecomposition()
1092
- await lesson1.start()
1093
- } catch (error) {
1094
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1095
- console.log("\n🚀 Próximamente:")
1096
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1097
- console.log("- Ejercicios prácticos que ilustren el concepto")
1098
- console.log("- Proyectos específicos para internalizar la idea")
1099
- }
1100
- return
1101
- } else if (enunciadoSeleccionado.id === 2) {
1102
- console.log(`${enunciadoSeleccionado.texto}`)
1103
- rl.close()
1104
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1105
-
1106
- try {
1107
- const lesson2 = new Lesson2ObjectRequests()
1108
- await lesson2.start()
1109
- } catch (error) {
1110
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1111
- console.log("\n🚀 Próximamente:")
1112
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1113
- console.log("- Ejercicios prácticos que ilustren el concepto")
1114
- console.log("- Proyectos específicos para internalizar la idea")
1115
- }
1116
- return
1117
- } else if (enunciadoSeleccionado.id === 3) {
1118
- console.log(`${enunciadoSeleccionado.texto}`)
1119
- rl.close()
1120
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1121
-
1122
- try {
1123
- const lesson3 = new Lesson3OnlyWay()
1124
- await lesson3.start()
1125
- } catch (error) {
1126
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1127
- console.log("\n🚀 Próximamente:")
1128
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1129
- console.log("- Ejercicios prácticos que ilustren el concepto")
1130
- console.log("- Proyectos específicos para internalizar la idea")
1131
- }
1132
- return
1133
- } else if (enunciadoSeleccionado.id === 4) {
1134
- console.log(`${enunciadoSeleccionado.texto}`)
1135
- rl.close()
1136
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1137
-
1138
- try {
1139
- const lesson4 = new Lesson4OperationSignatures()
1140
- await lesson4.start()
1141
- } catch (error) {
1142
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1143
- console.log("\n🚀 Próximamente:")
1144
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1145
- console.log("- Ejercicios prácticos que ilustren el concepto")
1146
- console.log("- Proyectos específicos para internalizar la idea")
1147
- }
1148
- return
1149
- } else if (enunciadoSeleccionado.id === 5) {
1150
- console.log(`${enunciadoSeleccionado.texto}`)
1151
- rl.close()
1152
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1153
-
1154
- try {
1155
- const lesson5 = new Lesson5InterfaceSet()
1156
- await lesson5.start()
1157
- } catch (error) {
1158
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1159
- console.log("\n🚀 Próximamente:")
1160
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1161
- console.log("- Ejercicios prácticos que ilustren el concepto")
1162
- console.log("- Proyectos específicos para internalizar la idea")
1163
- }
1164
- return
1165
- } else if (enunciadoSeleccionado.id === 6) {
1166
- console.log(`${enunciadoSeleccionado.texto}`)
1167
- rl.close()
1168
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1169
-
1170
- try {
1171
- const lesson6 = new Lesson6InterfaceDesign()
1172
- await lesson6.start()
1173
- } catch (error) {
1174
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1175
- console.log("\n🚀 Próximamente:")
1176
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1177
- console.log("- Ejercicios prácticos que ilustren el concepto")
1178
- console.log("- Proyectos específicos para internalizar la idea")
1179
- }
1180
- return
1181
- } else if (enunciadoSeleccionado.id === 7) {
1182
- console.log(`${enunciadoSeleccionado.texto}`)
1183
- rl.close()
1184
- await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
1185
-
1186
- try {
1187
- const lesson7 = new Lesson7ObjectDefinition()
1188
- await lesson7.start()
1189
- } catch (error) {
1190
- console.error("\n❌ Error al ejecutar la lección:", error.message)
1191
- console.log("\n🚀 Próximamente:")
1192
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1193
- console.log("- Ejercicios prácticos que ilustren el concepto")
1194
- console.log("- Proyectos específicos para internalizar la idea")
1195
- }
1196
- return
1197
- } else {
1198
- console.log("\n🚀 Próximamente:")
1199
- console.log("- Sesiones de estudio dirigidas basadas en este enunciado")
1200
- console.log("- Ejercicios prácticos que ilustren el concepto")
1201
- console.log("- Proyectos específicos para internalizar la idea")
1202
-
1203
- console.log("\n💭 Por ahora, reflexiona: ¿qué parte específica de este enunciado")
1204
- console.log(" te genera más curiosidad o confusión?")
1205
- }
1206
-
1207
- } else {
1208
- console.log("❌ Opción no válida. Elige un número entre 1 y 7.")
1209
- }
1210
-
1211
- rl.close()
1212
- } catch (error) {
1213
- console.error('Error:', error.message)
1214
- rl.close()
1215
- process.exit(1)
1216
- }
1217
- }
1218
-
1219
-
1220
- async function startProyectosSelector() {
1221
- // Always try interactive mode first, fall back only if it fails
1222
- try {
1223
- return await startProyectosSelectorInteractive()
1224
- } catch (error) {
1225
- // If interactive mode fails, fall back to numbered selection
1226
- console.log('\nModo interactivo no disponible, usando selección numérica...\n')
1227
- return await startProyectosSelectorFallback()
1228
- }
1229
- }
1230
-
1231
- async function startProyectosSelectorInteractive() {
1232
- // Check if setRawMode is available before trying to use it
1233
- if (typeof process.stdin.setRawMode !== 'function') {
1234
- throw new Error('setRawMode not available')
1235
- }
1236
-
1237
- console.log("\n🚀 Construir Proyectos")
1238
- console.log("Elige qué proyecto quieres construir:")
1239
-
1240
- const projects = [
1241
- { id: 1, title: "🔀 Construye una pull-request", type: 'pr' },
1242
- { id: 2, title: "🎨 Construye tu portafolio", type: 'portfolio' }
1243
- ]
1244
-
1245
- let selectedIndex = 0
1246
-
1247
- // Enable raw mode to capture arrow keys
1248
- try {
1249
- process.stdin.setRawMode(true)
1250
- process.stdin.resume()
1251
- process.stdin.setEncoding('utf8')
1252
- } catch (error) {
1253
- throw new Error('Failed to enable raw mode: ' + error.message)
1254
- }
1255
-
1256
- const renderOptions = () => {
1257
- // Clear previous output and move cursor to top
1258
- process.stdout.write('\x1b[2J')
1259
- process.stdout.write('\x1b[H')
1260
-
1261
- console.log("🚀 Construir Proyectos")
1262
- console.log("Elige qué proyecto quieres construir:")
1263
-
1264
- projects.forEach((project, index) => {
1265
- const isSelected = index === selectedIndex
1266
- const prefix = isSelected ? '▶ ' : ' '
1267
- const highlight = isSelected ? '\x1b[36m' : '\x1b[37m' // cyan for selected, white for normal
1268
- const reset = '\x1b[0m'
1269
-
1270
- console.log(`${highlight}${prefix}${project.title}${reset}`)
1271
- console.log("")
1272
- })
1273
- }
1274
-
1275
- return new Promise((resolve) => {
1276
- renderOptions()
1277
-
1278
- const onKeyPress = (key) => {
1279
- if (key === 'q' || key === '\x03') { // q or Ctrl+C
1280
- process.stdin.setRawMode(false)
1281
- process.stdin.removeListener('data', onKeyPress)
1282
- console.log("\nHecho con <3 por icarus.mx")
1283
- resolve()
1284
- return
1285
- }
1286
-
1287
- if (key === '\r' || key === '\n') { // Enter
1288
- process.stdin.setRawMode(false)
1289
- process.stdin.removeListener('data', onKeyPress)
1290
-
1291
- const selectedProject = projects[selectedIndex]
1292
-
1293
- // Clear screen
1294
- process.stdout.write('\x1b[2J')
1295
- process.stdout.write('\x1b[H')
1296
-
1297
- if (selectedProject.type === 'pr') {
1298
- startPullRequestTutorial().then(resolve)
1299
- } else if (selectedProject.type === 'portfolio') {
1300
- startPortfolioSelector().then(resolve)
1301
- }
1302
- return
1303
- }
1304
-
1305
- // Handle arrow keys (escape sequences)
1306
- if (key === '\u001b[A') { // Up arrow
1307
- selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : projects.length - 1
1308
- renderOptions()
1309
- } else if (key === '\u001b[B') { // Down arrow
1310
- selectedIndex = selectedIndex < projects.length - 1 ? selectedIndex + 1 : 0
1311
- renderOptions()
1312
- }
1313
- }
1314
-
1315
- process.stdin.on('data', onKeyPress)
1316
- })
1317
- }
1318
-
1319
- async function returnToMainMenu() {
1320
- const rl = createInterface({
1321
- input: process.stdin,
1322
- output: process.stdout
1323
- })
1324
-
1325
- const askQuestion = (question) => {
1326
- return new Promise((resolve) => {
1327
- rl.question(question, resolve)
1328
- })
1329
- }
1330
-
1331
- console.log("\n🏠 ¿Qué te gustaría hacer ahora?")
1332
- console.log("1. Volver al menú principal")
1333
- console.log("2. Salir")
1334
-
1335
- const respuesta = await askQuestion("\nElige una opción (1-2): ")
1336
-
1337
- if (respuesta === '1') {
1338
- rl.close()
1339
- console.clear()
1340
- await startMainMenu()
1341
- } else {
1342
- console.log("\nHecho con <3 por icarus.mx")
1343
- rl.close()
1344
- }
1345
- }
1346
-
1347
- async function startProyectosSelectorFallback() {
1348
- const rl = createInterface({
1349
- input: process.stdin,
1350
- output: process.stdout
1351
- })
1352
-
1353
- const askQuestion = (question) => {
1354
- return new Promise((resolve) => {
1355
- rl.question(question, resolve)
1356
- })
1357
- }
1358
-
1359
- try {
1360
- console.log("\n🚀 Construir Proyectos")
1361
- console.log("Elige qué proyecto quieres construir:")
1362
- console.log("")
1363
- console.log("1. 🔀 Construye una pull-request")
1364
- console.log("2. 🎨 Construye tu portafolio")
1365
- console.log("")
1366
-
1367
- const respuesta = await askQuestion("Elige una opción (1-2) o 'q' para salir: ")
1368
-
1369
- if (respuesta.toLowerCase() === 'q') {
1370
- console.log("Hecho con <3 por icarus.mx")
1371
- rl.close()
1372
- return
1373
- }
1374
-
1375
- const opcionSeleccionada = parseInt(respuesta)
1376
-
1377
- if (opcionSeleccionada === 1) {
1378
- rl.close()
1379
- await startPullRequestTutorial()
1380
- } else if (opcionSeleccionada === 2) {
1381
- rl.close()
1382
- await startPortfolioSelector()
1383
- } else {
1384
- console.log("❌ Opción no válida. Elige 1 o 2.")
1385
- rl.close()
1386
- }
1387
-
1388
- } catch (error) {
1389
- console.error('Error:', error.message)
1390
- rl.close()
1391
- process.exit(1)
1392
- }
1393
- }
1394
-
1395
- async function startPortfolioSelector() {
1396
- // Always try interactive mode first, fall back only if it fails
1397
- try {
1398
- return await startPortfolioSelectorInteractive()
1399
- } catch (error) {
1400
- // If interactive mode fails, fall back to numbered selection
1401
- console.log('\nModo interactivo no disponible, usando selección numérica...\n')
1402
- return await startPortfolioSelectorFallback()
1403
- }
1404
- }
1405
-
1406
- async function startPortfolioSelectorInteractive() {
1407
- // Check if setRawMode is available before trying to use it
1408
- if (typeof process.stdin.setRawMode !== 'function') {
1409
- throw new Error('setRawMode not available')
1410
- }
1411
-
1412
- console.log("\n🎨 Construye tu Portafolio")
1413
- console.log("Elige el nivel:")
1414
-
1415
- const projects = [
1416
- { id: 1, title: "🎨 Portafolio Personal", description: "Reto completo", level: 0 },
1417
- { id: 2, title: "🔓 Portafolio Nivel 1", description: "Solo navbar", level: 1 },
1418
- { id: 3, title: "🔓 Portafolio Nivel 2", description: "Navbar + hero", level: 2 },
1419
- { id: 4, title: "🔓 Portafolio Nivel 3", description: "Solución completa", level: 3 }
1420
- ]
1421
-
1422
- let selectedIndex = 0
1423
-
1424
- // Enable raw mode to capture arrow keys
1425
- try {
1426
- process.stdin.setRawMode(true)
1427
- process.stdin.resume()
1428
- process.stdin.setEncoding('utf8')
1429
- } catch (error) {
1430
- throw new Error('Failed to enable raw mode: ' + error.message)
1431
- }
1432
-
1433
- const renderOptions = () => {
1434
- // Clear previous output and move cursor to top
1435
- process.stdout.write('\x1b[2J')
1436
- process.stdout.write('\x1b[H')
1437
-
1438
- console.log("🎨 Construye tu Portafolio")
1439
- console.log("Elige el nivel:")
1440
-
1441
- projects.forEach((project, index) => {
1442
- const isSelected = index === selectedIndex
1443
- const prefix = isSelected ? '▶ ' : ' '
1444
- const highlight = isSelected ? '\x1b[36m' : '\x1b[37m' // cyan for selected, white for normal
1445
- const reset = '\x1b[0m'
1446
-
1447
- console.log(`${highlight}${prefix}${project.title}${reset}`)
1448
- if (isSelected) {
1449
- console.log(`${highlight} ${project.description}${reset}`)
1450
- }
1451
- console.log("")
1452
- })
1453
- }
1454
-
1455
- return new Promise((resolve) => {
1456
- renderOptions()
1457
-
1458
- const onKeyPress = (key) => {
1459
- if (key === 'q' || key === '\x03') { // q or Ctrl+C
1460
- process.stdin.setRawMode(false)
1461
- process.stdin.removeListener('data', onKeyPress)
1462
- console.log("\nHecho con <3 por icarus.mx")
1463
- resolve()
1464
- return
1465
- }
1466
-
1467
- if (key === '\r' || key === '\n') { // Enter
1468
- process.stdin.setRawMode(false)
1469
- process.stdin.removeListener('data', onKeyPress)
1470
-
1471
- const selectedProject = projects[selectedIndex]
1472
- const level = selectedProject.level
1473
-
1474
- // Clear screen
1475
- process.stdout.write('\x1b[2J')
1476
- process.stdout.write('\x1b[H')
1477
-
1478
- // Check if we're in an existing Creta project
1479
- if (level > 0 && isInCretaProject()) {
1480
- unstuckProject(level).then(resolve)
1481
- } else {
1482
- createPortfolioProject(level).then(resolve)
1483
- }
1484
- return
1485
- }
1486
-
1487
- // Handle arrow keys (escape sequences)
1488
- if (key === '\u001b[A') { // Up arrow
1489
- selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : projects.length - 1
1490
- renderOptions()
1491
- } else if (key === '\u001b[B') { // Down arrow
1492
- selectedIndex = selectedIndex < projects.length - 1 ? selectedIndex + 1 : 0
1493
- renderOptions()
1494
- }
1495
- }
1496
-
1497
- process.stdin.on('data', onKeyPress)
1498
- })
1499
- }
1500
-
1501
- async function startPortfolioSelectorFallback() {
1502
- const rl = createInterface({
1503
- input: process.stdin,
1504
- output: process.stdout
1505
- })
1506
-
1507
- const askQuestion = (question) => {
1508
- return new Promise((resolve) => {
1509
- rl.question(question, resolve)
1510
- })
1511
- }
1512
-
1513
- try {
1514
- console.log("\n🎨 Construye tu Portafolio")
1515
- console.log("Elige el nivel:")
1516
- console.log("")
1517
- console.log("1. 🎨 Portafolio Personal - Reto completo")
1518
- console.log("2. 🔓 Portafolio Nivel 1 - Solo navbar")
1519
- console.log("3. 🔓 Portafolio Nivel 2 - Navbar + hero")
1520
- console.log("4. 🔓 Portafolio Nivel 3 - Solución completa")
1521
- console.log("")
1522
-
1523
- const respuesta = await askQuestion("Elige una opción (1-4) o 'q' para salir: ")
1524
-
1525
- if (respuesta.toLowerCase() === 'q') {
1526
- console.log("Hecho con <3 por icarus.mx")
1527
- rl.close()
1528
- return
1529
- }
1530
-
1531
- const opcionSeleccionada = parseInt(respuesta)
1532
-
1533
- if (opcionSeleccionada >= 1 && opcionSeleccionada <= 4) {
1534
- rl.close()
1535
-
1536
- // Map selection to portfolio level
1537
- const level = opcionSeleccionada === 1 ? 0 : opcionSeleccionada - 1
1538
-
1539
- // Check if we're in an existing Creta project
1540
- if (level > 0 && isInCretaProject()) {
1541
- await unstuckProject(level)
1542
- } else {
1543
- await createPortfolioProject(level)
1544
- }
1545
- } else {
1546
- console.log("❌ Opción no válida. Elige un número entre 1 y 4.")
1547
- rl.close()
1548
- }
1549
-
1550
- } catch (error) {
1551
- console.error('Error:', error.message)
1552
- rl.close()
1553
- process.exit(1)
1554
- }
1555
- }
1556
-
1557
- async function startPullRequestTutorial() {
1558
- try {
1559
- const tutorial = new PullRequestTutorial()
1560
- await tutorial.start()
1561
-
1562
- // Volver al menú principal después de completar el tutorial
1563
- await returnToMainMenu()
1564
- } catch (error) {
1565
- console.error("\n❌ Error al ejecutar el tutorial:", error.message)
1566
- console.log("\nSi el problema persiste, contacta al equipo de Creta.")
1567
-
1568
- const rl = createInterface({
1569
- input: process.stdin,
1570
- output: process.stdout
1571
- })
1572
-
1573
- await new Promise((resolve) => {
1574
- rl.question("\nPresiona Enter para volver al menú principal...", () => {
1575
- rl.close()
1576
- resolve()
1577
- })
1578
- })
1579
-
1580
- await startMainMenu()
1581
- }
1582
- }