@icarusmx/creta 1.5.1 → 1.5.4
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.
- package/bin/creta.js +38 -27
- package/lib/executors/ExercisesExecutor.js +271 -0
- package/lib/utils/user-state.js +15 -0
- package/package.json +1 -1
package/bin/creta.js
CHANGED
|
@@ -9,13 +9,15 @@ import { CretaCodeSession } from '../lib/session.js'
|
|
|
9
9
|
import { PullRequestTutorial } from '../lib/pr-tutorial.js'
|
|
10
10
|
import { CommandHelpExecutor } from '../lib/executors/CommandHelpExecutor.js'
|
|
11
11
|
import { PapersExecutor } from '../lib/papers/PapersExecutor.js'
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
12
|
+
import { ExercisesExecutor } from '../lib/executors/ExercisesExecutor.js'
|
|
13
|
+
import { LessonBuilder } from '../lib/builders/LessonBuilder.js'
|
|
14
|
+
import { LESSON_1_SYSTEM_DECOMPOSITION } from '../lib/data/lessons/lesson1-system-decomposition.js'
|
|
15
|
+
import { LESSON_2_OBJECT_REQUESTS } from '../lib/data/lessons/lesson2-object-requests.js'
|
|
16
|
+
import { LESSON_3_ONLY_WAY } from '../lib/data/lessons/lesson3-only-way.js'
|
|
17
|
+
import { LESSON_4_OPERATION_SIGNATURES } from '../lib/data/lessons/lesson4-operation-signatures.js'
|
|
18
|
+
import { LESSON_5_INTERFACE_SET } from '../lib/data/lessons/lesson5-interface-set.js'
|
|
19
|
+
import { LESSON_6_INTERFACE_DESIGN } from '../lib/data/lessons/lesson6-interface-design.js'
|
|
20
|
+
import { LESSON_7_OBJECT_DEFINITION } from '../lib/data/lessons/lesson7-object-definition.js'
|
|
19
21
|
|
|
20
22
|
const ENUNCIADOS = [
|
|
21
23
|
{
|
|
@@ -637,7 +639,8 @@ async function startMainMenuInteractive() {
|
|
|
637
639
|
{ id: 1, title: "Aprender sintaxis" },
|
|
638
640
|
{ id: 2, title: "Aprender conceptos de diseño" },
|
|
639
641
|
{ id: 3, title: "Construir proyectos" },
|
|
640
|
-
{ id: 4, title: "
|
|
642
|
+
{ id: 4, title: "Recrear papers clásicos" },
|
|
643
|
+
{ id: 5, title: "Practicar con ejercicios" }
|
|
641
644
|
]
|
|
642
645
|
|
|
643
646
|
let selectedIndex = 0
|
|
@@ -710,6 +713,9 @@ Salgamos de este laberinto 🏛️
|
|
|
710
713
|
} else if (selectedOption.id === 4) {
|
|
711
714
|
const executor = new PapersExecutor()
|
|
712
715
|
executor.execute().then(resolve)
|
|
716
|
+
} else if (selectedOption.id === 5) {
|
|
717
|
+
const executor = new ExercisesExecutor()
|
|
718
|
+
executor.execute().then(resolve)
|
|
713
719
|
}
|
|
714
720
|
return
|
|
715
721
|
}
|
|
@@ -746,10 +752,11 @@ async function startMainMenuFallback() {
|
|
|
746
752
|
console.log("1. Aprender sintaxis")
|
|
747
753
|
console.log("2. Aprender conceptos de diseño")
|
|
748
754
|
console.log("3. Construir proyectos")
|
|
749
|
-
console.log("4.
|
|
755
|
+
console.log("4. Recrear papers clásicos")
|
|
756
|
+
console.log("5. Practicar con ejercicios")
|
|
750
757
|
console.log("")
|
|
751
758
|
|
|
752
|
-
const respuesta = await askQuestion("Elige una opción (1-
|
|
759
|
+
const respuesta = await askQuestion("Elige una opción (1-5) o 'q' para salir: ")
|
|
753
760
|
|
|
754
761
|
if (respuesta.toLowerCase() === 'q') {
|
|
755
762
|
console.log("Hecho con <3 por icarus.mx")
|
|
@@ -772,8 +779,12 @@ async function startMainMenuFallback() {
|
|
|
772
779
|
rl.close()
|
|
773
780
|
const executor = new PapersExecutor()
|
|
774
781
|
await executor.execute()
|
|
782
|
+
} else if (opcionSeleccionada === 5) {
|
|
783
|
+
rl.close()
|
|
784
|
+
const executor = new ExercisesExecutor()
|
|
785
|
+
await executor.execute()
|
|
775
786
|
} else {
|
|
776
|
-
console.log("❌ Opción no válida. Elige 1, 2, 3 o
|
|
787
|
+
console.log("❌ Opción no válida. Elige 1, 2, 3, 4 o 5.")
|
|
777
788
|
rl.close()
|
|
778
789
|
}
|
|
779
790
|
|
|
@@ -892,7 +903,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
892
903
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
893
904
|
|
|
894
905
|
try {
|
|
895
|
-
const lesson1 = new
|
|
906
|
+
const lesson1 = new LessonBuilder(LESSON_1_SYSTEM_DECOMPOSITION)
|
|
896
907
|
await lesson1.start()
|
|
897
908
|
await returnToMainMenu()
|
|
898
909
|
} catch (error) {
|
|
@@ -908,7 +919,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
908
919
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
909
920
|
|
|
910
921
|
try {
|
|
911
|
-
const lesson1 = new
|
|
922
|
+
const lesson1 = new LessonBuilder(LESSON_1_SYSTEM_DECOMPOSITION)
|
|
912
923
|
await lesson1.start()
|
|
913
924
|
await returnToMainMenu()
|
|
914
925
|
} catch (error) {
|
|
@@ -924,7 +935,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
924
935
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
925
936
|
|
|
926
937
|
try {
|
|
927
|
-
const lesson2 = new
|
|
938
|
+
const lesson2 = new LessonBuilder(LESSON_2_OBJECT_REQUESTS)
|
|
928
939
|
await lesson2.start()
|
|
929
940
|
await returnToMainMenu()
|
|
930
941
|
} catch (error) {
|
|
@@ -940,7 +951,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
940
951
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
941
952
|
|
|
942
953
|
try {
|
|
943
|
-
const lesson3 = new
|
|
954
|
+
const lesson3 = new LessonBuilder(LESSON_3_ONLY_WAY)
|
|
944
955
|
await lesson3.start()
|
|
945
956
|
await returnToMainMenu()
|
|
946
957
|
} catch (error) {
|
|
@@ -956,7 +967,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
956
967
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
957
968
|
|
|
958
969
|
try {
|
|
959
|
-
const lesson4 = new
|
|
970
|
+
const lesson4 = new LessonBuilder(LESSON_4_OPERATION_SIGNATURES)
|
|
960
971
|
await lesson4.start()
|
|
961
972
|
await returnToMainMenu()
|
|
962
973
|
} catch (error) {
|
|
@@ -972,7 +983,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
972
983
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
973
984
|
|
|
974
985
|
try {
|
|
975
|
-
const lesson5 = new
|
|
986
|
+
const lesson5 = new LessonBuilder(LESSON_5_INTERFACE_SET)
|
|
976
987
|
await lesson5.start()
|
|
977
988
|
await returnToMainMenu()
|
|
978
989
|
} catch (error) {
|
|
@@ -988,7 +999,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
988
999
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
989
1000
|
|
|
990
1001
|
try {
|
|
991
|
-
const lesson6 = new
|
|
1002
|
+
const lesson6 = new LessonBuilder(LESSON_6_INTERFACE_DESIGN)
|
|
992
1003
|
await lesson6.start()
|
|
993
1004
|
await returnToMainMenu()
|
|
994
1005
|
} catch (error) {
|
|
@@ -1004,7 +1015,7 @@ async function startEnunciadosSelectorInteractive() {
|
|
|
1004
1015
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1005
1016
|
|
|
1006
1017
|
try {
|
|
1007
|
-
const lesson7 = new
|
|
1018
|
+
const lesson7 = new LessonBuilder(LESSON_7_OBJECT_DEFINITION)
|
|
1008
1019
|
await lesson7.start()
|
|
1009
1020
|
await returnToMainMenu()
|
|
1010
1021
|
} catch (error) {
|
|
@@ -1098,7 +1109,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1098
1109
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1099
1110
|
|
|
1100
1111
|
try {
|
|
1101
|
-
const lesson1 = new
|
|
1112
|
+
const lesson1 = new LessonBuilder(LESSON_1_SYSTEM_DECOMPOSITION)
|
|
1102
1113
|
await lesson1.start()
|
|
1103
1114
|
} catch (error) {
|
|
1104
1115
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -1114,7 +1125,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1114
1125
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1115
1126
|
|
|
1116
1127
|
try {
|
|
1117
|
-
const lesson1 = new
|
|
1128
|
+
const lesson1 = new LessonBuilder(LESSON_1_SYSTEM_DECOMPOSITION)
|
|
1118
1129
|
await lesson1.start()
|
|
1119
1130
|
} catch (error) {
|
|
1120
1131
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -1130,7 +1141,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1130
1141
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1131
1142
|
|
|
1132
1143
|
try {
|
|
1133
|
-
const lesson2 = new
|
|
1144
|
+
const lesson2 = new LessonBuilder(LESSON_2_OBJECT_REQUESTS)
|
|
1134
1145
|
await lesson2.start()
|
|
1135
1146
|
} catch (error) {
|
|
1136
1147
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -1146,7 +1157,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1146
1157
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1147
1158
|
|
|
1148
1159
|
try {
|
|
1149
|
-
const lesson3 = new
|
|
1160
|
+
const lesson3 = new LessonBuilder(LESSON_3_ONLY_WAY)
|
|
1150
1161
|
await lesson3.start()
|
|
1151
1162
|
} catch (error) {
|
|
1152
1163
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -1162,7 +1173,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1162
1173
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1163
1174
|
|
|
1164
1175
|
try {
|
|
1165
|
-
const lesson4 = new
|
|
1176
|
+
const lesson4 = new LessonBuilder(LESSON_4_OPERATION_SIGNATURES)
|
|
1166
1177
|
await lesson4.start()
|
|
1167
1178
|
} catch (error) {
|
|
1168
1179
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -1178,7 +1189,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1178
1189
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1179
1190
|
|
|
1180
1191
|
try {
|
|
1181
|
-
const lesson5 = new
|
|
1192
|
+
const lesson5 = new LessonBuilder(LESSON_5_INTERFACE_SET)
|
|
1182
1193
|
await lesson5.start()
|
|
1183
1194
|
} catch (error) {
|
|
1184
1195
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -1194,7 +1205,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1194
1205
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1195
1206
|
|
|
1196
1207
|
try {
|
|
1197
|
-
const lesson6 = new
|
|
1208
|
+
const lesson6 = new LessonBuilder(LESSON_6_INTERFACE_DESIGN)
|
|
1198
1209
|
await lesson6.start()
|
|
1199
1210
|
} catch (error) {
|
|
1200
1211
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -1210,7 +1221,7 @@ async function startEnunciadosSelectorFallback() {
|
|
|
1210
1221
|
await new Promise(resolve => setTimeout(resolve, 1500)) // Brief pause
|
|
1211
1222
|
|
|
1212
1223
|
try {
|
|
1213
|
-
const lesson7 = new
|
|
1224
|
+
const lesson7 = new LessonBuilder(LESSON_7_OBJECT_DEFINITION)
|
|
1214
1225
|
await lesson7.start()
|
|
1215
1226
|
} catch (error) {
|
|
1216
1227
|
console.error("\n❌ Error al ejecutar la lección:", error.message)
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { createInterface } from 'readline'
|
|
2
|
+
import { execSync } from 'child_process'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
+
const __dirname = path.dirname(__filename)
|
|
9
|
+
|
|
10
|
+
export class ExercisesExecutor {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.exercises = [
|
|
13
|
+
{
|
|
14
|
+
id: 1,
|
|
15
|
+
title: "Array & Object Manipulation Warmups",
|
|
16
|
+
description: "JavaScript fundamentals with LazyVim tips",
|
|
17
|
+
file: "array-object-manipulation.md",
|
|
18
|
+
lines: 1282,
|
|
19
|
+
topics: ["JavaScript", "Arrays", "Objects", "LazyVim"]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 2,
|
|
23
|
+
title: "Git Stash Workflow",
|
|
24
|
+
description: "Wrong branch recovery patterns",
|
|
25
|
+
file: "git-stash-workflow.md",
|
|
26
|
+
lines: 427,
|
|
27
|
+
topics: ["Git", "Stash", "Branching", "Workflow"]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 3,
|
|
31
|
+
title: "Railway Deployment Guide",
|
|
32
|
+
description: "Deploy Node.js apps with Railway CLI",
|
|
33
|
+
file: "railway-deployment.md",
|
|
34
|
+
lines: 500,
|
|
35
|
+
topics: ["Railway", "Deployment", "Node.js", "DevOps"]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async execute() {
|
|
41
|
+
// Check if setRawMode is available
|
|
42
|
+
if (typeof process.stdin.setRawMode === 'function') {
|
|
43
|
+
return await this.executeInteractive()
|
|
44
|
+
} else {
|
|
45
|
+
return await this.executeFallback()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async executeInteractive() {
|
|
50
|
+
console.log("\n📝 Ejercicios de Práctica")
|
|
51
|
+
console.log("Elige qué ejercicio quieres practicar:")
|
|
52
|
+
console.log("")
|
|
53
|
+
|
|
54
|
+
let selectedIndex = 0
|
|
55
|
+
|
|
56
|
+
// Enable raw mode
|
|
57
|
+
process.stdin.setRawMode(true)
|
|
58
|
+
process.stdin.resume()
|
|
59
|
+
process.stdin.setEncoding('utf8')
|
|
60
|
+
|
|
61
|
+
const renderOptions = () => {
|
|
62
|
+
// Clear screen
|
|
63
|
+
process.stdout.write('\x1b[2J')
|
|
64
|
+
process.stdout.write('\x1b[H')
|
|
65
|
+
|
|
66
|
+
console.log("📝 Ejercicios de Práctica")
|
|
67
|
+
console.log("Elige qué ejercicio quieres practicar:")
|
|
68
|
+
console.log("")
|
|
69
|
+
|
|
70
|
+
this.exercises.forEach((exercise, index) => {
|
|
71
|
+
const isSelected = index === selectedIndex
|
|
72
|
+
const prefix = isSelected ? '▶ ' : ' '
|
|
73
|
+
const highlight = isSelected ? '\x1b[36m' : '\x1b[37m'
|
|
74
|
+
const reset = '\x1b[0m'
|
|
75
|
+
|
|
76
|
+
console.log(`${highlight}${prefix}${exercise.id}. ${exercise.title}${reset}`)
|
|
77
|
+
if (isSelected) {
|
|
78
|
+
console.log(`${highlight} ${exercise.description} (${exercise.lines} líneas)${reset}`)
|
|
79
|
+
}
|
|
80
|
+
console.log("")
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
console.log("\n💡 Tip: Estos archivos están optimizados para LazyVim")
|
|
84
|
+
console.log(" Usa 'nvim' para abrirlos y aprovechar los fold markers")
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
renderOptions()
|
|
89
|
+
|
|
90
|
+
const onKeyPress = async (key) => {
|
|
91
|
+
if (key === 'q' || key === '\x03') { // q or Ctrl+C
|
|
92
|
+
process.stdin.setRawMode(false)
|
|
93
|
+
process.stdin.removeListener('data', onKeyPress)
|
|
94
|
+
console.log("\nHecho con <3 por icarus.mx")
|
|
95
|
+
resolve()
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (key === '\r' || key === '\n') { // Enter
|
|
100
|
+
process.stdin.setRawMode(false)
|
|
101
|
+
process.stdin.removeListener('data', onKeyPress)
|
|
102
|
+
|
|
103
|
+
const selectedExercise = this.exercises[selectedIndex]
|
|
104
|
+
|
|
105
|
+
// Clear screen
|
|
106
|
+
process.stdout.write('\x1b[2J')
|
|
107
|
+
process.stdout.write('\x1b[H')
|
|
108
|
+
|
|
109
|
+
await this.openExercise(selectedExercise)
|
|
110
|
+
resolve()
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Arrow keys
|
|
115
|
+
if (key === '\u001b[A') { // Up
|
|
116
|
+
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : this.exercises.length - 1
|
|
117
|
+
renderOptions()
|
|
118
|
+
} else if (key === '\u001b[B') { // Down
|
|
119
|
+
selectedIndex = selectedIndex < this.exercises.length - 1 ? selectedIndex + 1 : 0
|
|
120
|
+
renderOptions()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
process.stdin.on('data', onKeyPress)
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async executeFallback() {
|
|
129
|
+
const rl = createInterface({
|
|
130
|
+
input: process.stdin,
|
|
131
|
+
output: process.stdout
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const askQuestion = (question) => {
|
|
135
|
+
return new Promise((resolve) => {
|
|
136
|
+
rl.question(question, resolve)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
console.log("\n📝 Ejercicios de Práctica")
|
|
142
|
+
console.log("Elige qué ejercicio quieres practicar:")
|
|
143
|
+
console.log("")
|
|
144
|
+
|
|
145
|
+
this.exercises.forEach((exercise) => {
|
|
146
|
+
console.log(`${exercise.id}. ${exercise.title}`)
|
|
147
|
+
console.log(` ${exercise.description} (${exercise.lines} líneas)`)
|
|
148
|
+
console.log("")
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
console.log("💡 Tip: Estos archivos están optimizados para LazyVim")
|
|
152
|
+
console.log(" Usa 'nvim' para abrirlos y aprovechar los fold markers")
|
|
153
|
+
console.log("")
|
|
154
|
+
|
|
155
|
+
const respuesta = await askQuestion(`Elige una opción (1-${this.exercises.length}) o 'q' para salir: `)
|
|
156
|
+
|
|
157
|
+
if (respuesta.toLowerCase() === 'q') {
|
|
158
|
+
console.log("Hecho con <3 por icarus.mx")
|
|
159
|
+
rl.close()
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const opcionSeleccionada = parseInt(respuesta)
|
|
164
|
+
|
|
165
|
+
if (opcionSeleccionada >= 1 && opcionSeleccionada <= this.exercises.length) {
|
|
166
|
+
const selectedExercise = this.exercises[opcionSeleccionada - 1]
|
|
167
|
+
rl.close()
|
|
168
|
+
await this.openExercise(selectedExercise)
|
|
169
|
+
} else {
|
|
170
|
+
console.log(`❌ Opción no válida. Elige un número entre 1 y ${this.exercises.length}.`)
|
|
171
|
+
rl.close()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error('Error:', error.message)
|
|
176
|
+
rl.close()
|
|
177
|
+
process.exit(1)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async openExercise(exercise) {
|
|
182
|
+
const exercisePath = path.join(__dirname, '..', 'exercises', exercise.file)
|
|
183
|
+
|
|
184
|
+
console.log(`\n📖 ${exercise.title}`)
|
|
185
|
+
console.log("=" .repeat(50))
|
|
186
|
+
console.log("")
|
|
187
|
+
console.log(`📍 Ubicación: ${exercisePath}`)
|
|
188
|
+
console.log(`📊 Líneas: ${exercise.lines}`)
|
|
189
|
+
console.log(`🏷️ Temas: ${exercise.topics.join(', ')}`)
|
|
190
|
+
console.log("")
|
|
191
|
+
|
|
192
|
+
// Check if file exists
|
|
193
|
+
if (!fs.existsSync(exercisePath)) {
|
|
194
|
+
console.log(`❌ Error: No se encontró el archivo ${exercise.file}`)
|
|
195
|
+
console.log("")
|
|
196
|
+
console.log("💡 Tip: Asegúrate de estar ejecutando Creta desde el directorio correcto")
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log("📚 Opciones:")
|
|
201
|
+
console.log("")
|
|
202
|
+
console.log("1. Abrir con nvim (recomendado para aprovechar fold markers)")
|
|
203
|
+
console.log("2. Mostrar la ruta (copiar al portapapeles)")
|
|
204
|
+
console.log("3. Ver los primeros 20 líneas")
|
|
205
|
+
console.log("")
|
|
206
|
+
|
|
207
|
+
const rl = createInterface({
|
|
208
|
+
input: process.stdin,
|
|
209
|
+
output: process.stdout
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
const askQuestion = (question) => {
|
|
213
|
+
return new Promise((resolve) => {
|
|
214
|
+
rl.question(question, resolve)
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const choice = await askQuestion("Elige una opción (1-3) o Enter para abrir con nvim: ")
|
|
219
|
+
rl.close()
|
|
220
|
+
|
|
221
|
+
if (choice === '' || choice === '1') {
|
|
222
|
+
// Open with nvim
|
|
223
|
+
console.log("\n🚀 Abriendo con nvim...")
|
|
224
|
+
console.log("💡 Tip: Usa 'zM' para cerrar todos los folds y ver la estructura")
|
|
225
|
+
console.log("")
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
// Try to open with nvim
|
|
229
|
+
execSync(`nvim "${exercisePath}"`, { stdio: 'inherit' })
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.log("\n⚠️ No se pudo abrir nvim. Intentando con el editor por defecto...")
|
|
232
|
+
try {
|
|
233
|
+
execSync(`$EDITOR "${exercisePath}" || vi "${exercisePath}"`, { stdio: 'inherit' })
|
|
234
|
+
} catch (fallbackError) {
|
|
235
|
+
console.log("\n❌ No se pudo abrir ningún editor.")
|
|
236
|
+
console.log(`📍 Abre manualmente: ${exercisePath}`)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} else if (choice === '2') {
|
|
240
|
+
// Show path
|
|
241
|
+
console.log("\n📋 Ruta copiada al portapapeles (en algunos sistemas):")
|
|
242
|
+
console.log(exercisePath)
|
|
243
|
+
console.log("")
|
|
244
|
+
console.log("💡 Tip: Usa este comando para abrirlo:")
|
|
245
|
+
console.log(` nvim "${exercisePath}"`)
|
|
246
|
+
|
|
247
|
+
// Try to copy to clipboard (macOS)
|
|
248
|
+
try {
|
|
249
|
+
execSync(`echo "${exercisePath}" | pbcopy`, { stdio: 'ignore' })
|
|
250
|
+
console.log("✓ Ruta copiada al portapapeles (macOS)")
|
|
251
|
+
} catch (error) {
|
|
252
|
+
// Silently fail if pbcopy not available
|
|
253
|
+
}
|
|
254
|
+
} else if (choice === '3') {
|
|
255
|
+
// Show preview
|
|
256
|
+
console.log("\n📄 Primeras 20 líneas:")
|
|
257
|
+
console.log("=" .repeat(50))
|
|
258
|
+
try {
|
|
259
|
+
const preview = execSync(`head -20 "${exercisePath}"`, { encoding: 'utf8' })
|
|
260
|
+
console.log(preview)
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.log("❌ Error al leer el archivo")
|
|
263
|
+
}
|
|
264
|
+
console.log("=" .repeat(50))
|
|
265
|
+
console.log("")
|
|
266
|
+
console.log(`💡 Abre el archivo completo con: nvim "${exercisePath}"`)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.log("")
|
|
270
|
+
}
|
|
271
|
+
}
|
package/lib/utils/user-state.js
CHANGED
|
@@ -66,6 +66,7 @@ export class UserState {
|
|
|
66
66
|
|
|
67
67
|
static setName(name) {
|
|
68
68
|
const state = loadState()
|
|
69
|
+
if (!state) return null
|
|
69
70
|
state.name = name
|
|
70
71
|
saveState(state)
|
|
71
72
|
return state
|
|
@@ -73,6 +74,18 @@ export class UserState {
|
|
|
73
74
|
|
|
74
75
|
static updateLastSeen() {
|
|
75
76
|
const state = loadState()
|
|
77
|
+
if (!state) {
|
|
78
|
+
console.error('Warning: Could not load user state')
|
|
79
|
+
return {
|
|
80
|
+
name: null,
|
|
81
|
+
createdAt: new Date().toISOString(),
|
|
82
|
+
lessonsCompleted: [],
|
|
83
|
+
projectsCreated: [],
|
|
84
|
+
discord: null,
|
|
85
|
+
stats: { totalSessions: 0, lastSeen: null },
|
|
86
|
+
lastSeen: null
|
|
87
|
+
}
|
|
88
|
+
}
|
|
76
89
|
const now = new Date().toISOString()
|
|
77
90
|
state.lastSeen = now
|
|
78
91
|
state.stats = state.stats || { totalSessions: 0, lastSeen: null }
|
|
@@ -84,6 +97,7 @@ export class UserState {
|
|
|
84
97
|
|
|
85
98
|
static addCompletedLesson(lessonId) {
|
|
86
99
|
const state = loadState()
|
|
100
|
+
if (!state) return null
|
|
87
101
|
const alreadyCompleted = state.lessonsCompleted.some(
|
|
88
102
|
lesson => typeof lesson === 'object' ? lesson.id === lessonId : lesson === lessonId
|
|
89
103
|
)
|
|
@@ -100,6 +114,7 @@ export class UserState {
|
|
|
100
114
|
|
|
101
115
|
static addProject(projectName) {
|
|
102
116
|
const state = loadState()
|
|
117
|
+
if (!state) return null
|
|
103
118
|
state.projectsCreated = state.projectsCreated || []
|
|
104
119
|
state.projectsCreated.push({
|
|
105
120
|
name: projectName,
|