@icarusmx/creta 1.4.18 → 1.5.1

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.
@@ -0,0 +1,276 @@
1
+ import { createInterface } from 'readline'
2
+ import { spawnSync } from 'child_process'
3
+ import chalk from 'chalk'
4
+ import path from 'path'
5
+ import { fileURLToPath } from 'url'
6
+ import { AccessControl } from './AccessControl.js'
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = path.dirname(__filename)
10
+
11
+ export class PapersExecutor {
12
+ constructor() {
13
+ this.accessControl = new AccessControl()
14
+ }
15
+
16
+ async execute() {
17
+ // Try interactive mode first
18
+ try {
19
+ await this.showInteractiveMenu()
20
+ } catch (error) {
21
+ // Fallback to numbered selection
22
+ await this.showFallbackMenu()
23
+ }
24
+ }
25
+
26
+ async showInteractiveMenu() {
27
+ // Check if setRawMode is available
28
+ if (typeof process.stdin.setRawMode !== 'function') {
29
+ throw new Error('setRawMode not available')
30
+ }
31
+
32
+ const papers = this.accessControl.getAvailablePapers()
33
+ let selectedIndex = 0
34
+
35
+ // Enable raw mode
36
+ process.stdin.setRawMode(true)
37
+ process.stdin.resume()
38
+ process.stdin.setEncoding('utf8')
39
+
40
+ const renderMenu = () => {
41
+ process.stdout.write('\x1b[2J')
42
+ process.stdout.write('\x1b[H')
43
+
44
+ console.log(chalk.bold.cyan('\n📄 Papers Clásicos de Computer Science\n'))
45
+ console.log(chalk.dim('Recrea los papers que cambiaron la historia de la tecnología'))
46
+ console.log(chalk.dim('─'.repeat(60)))
47
+ console.log('')
48
+
49
+ papers.forEach((paper, index) => {
50
+ const isSelected = index === selectedIndex
51
+ const prefix = isSelected ? '▶ ' : ' '
52
+
53
+ let statusIcon = ''
54
+ let statusColor = chalk.white
55
+
56
+ if (paper.completed) {
57
+ statusIcon = '✅ '
58
+ statusColor = chalk.green
59
+ } else if (paper.unlocked) {
60
+ statusIcon = '🔓 '
61
+ statusColor = chalk.cyan
62
+ } else {
63
+ statusIcon = '🔒 '
64
+ statusColor = chalk.dim
65
+ }
66
+
67
+ const highlight = isSelected ? statusColor.bold : statusColor
68
+
69
+ console.log(highlight(`${prefix}${statusIcon}${paper.name}`))
70
+ console.log(highlight(` ${paper.description}`))
71
+
72
+ if (!paper.unlocked) {
73
+ console.log(chalk.dim(` Requisito: ${paper.requirement}`))
74
+ }
75
+
76
+ console.log('')
77
+ })
78
+
79
+ console.log(chalk.dim('\n↑↓: Navegar | Enter: Seleccionar | q: Salir'))
80
+ }
81
+
82
+ return new Promise((resolve) => {
83
+ renderMenu()
84
+
85
+ const onKeyPress = async (key) => {
86
+ if (key === 'q' || key === '\x03') {
87
+ process.stdin.setRawMode(false)
88
+ process.stdin.removeListener('data', onKeyPress)
89
+ console.log(chalk.dim('\nHecho con <3 por icarus.mx'))
90
+ resolve()
91
+ return
92
+ }
93
+
94
+ if (key === '\r' || key === '\n') {
95
+ const selectedPaper = papers[selectedIndex]
96
+
97
+ if (!selectedPaper.unlocked) {
98
+ process.stdout.write('\x1b[2J')
99
+ process.stdout.write('\x1b[H')
100
+ console.log(chalk.yellow(`\n🔒 Este paper está bloqueado\n`))
101
+ console.log(chalk.dim(`Requisito: ${selectedPaper.requirement}\n`))
102
+
103
+ const rl = createInterface({
104
+ input: process.stdin,
105
+ output: process.stdout
106
+ })
107
+
108
+ process.stdin.setRawMode(false)
109
+ process.stdin.removeListener('data', onKeyPress)
110
+
111
+ await new Promise(r => {
112
+ rl.question(chalk.dim('Presiona Enter para volver...'), () => {
113
+ rl.close()
114
+ r()
115
+ })
116
+ })
117
+
118
+ process.stdin.setRawMode(true)
119
+ process.stdin.on('data', onKeyPress)
120
+ renderMenu()
121
+ return
122
+ }
123
+
124
+ process.stdin.setRawMode(false)
125
+ process.stdin.removeListener('data', onKeyPress)
126
+
127
+ await this.launchPaper(selectedPaper.id)
128
+ resolve()
129
+ return
130
+ }
131
+
132
+ // Arrow keys
133
+ if (key === '\u001b[A') {
134
+ selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : papers.length - 1
135
+ renderMenu()
136
+ } else if (key === '\u001b[B') {
137
+ selectedIndex = selectedIndex < papers.length - 1 ? selectedIndex + 1 : 0
138
+ renderMenu()
139
+ }
140
+ }
141
+
142
+ process.stdin.on('data', onKeyPress)
143
+ })
144
+ }
145
+
146
+ async showFallbackMenu() {
147
+ const rl = createInterface({
148
+ input: process.stdin,
149
+ output: process.stdout
150
+ })
151
+
152
+ const askQuestion = (question) => {
153
+ return new Promise((resolve) => {
154
+ rl.question(question, resolve)
155
+ })
156
+ }
157
+
158
+ try {
159
+ console.log(chalk.bold.cyan('\n📄 Papers Clásicos de Computer Science\n'))
160
+ console.log(chalk.dim('Recrea los papers que cambiaron la historia de la tecnología'))
161
+ console.log(chalk.dim('─'.repeat(60)))
162
+ console.log('')
163
+
164
+ const papers = this.accessControl.getAvailablePapers()
165
+
166
+ papers.forEach((paper, index) => {
167
+ let statusIcon = paper.completed ? '✅' : (paper.unlocked ? '🔓' : '🔒')
168
+
169
+ console.log(`${index + 1}. ${statusIcon} ${paper.name}`)
170
+ console.log(` ${paper.description}`)
171
+
172
+ if (!paper.unlocked) {
173
+ console.log(chalk.dim(` Requisito: ${paper.requirement}`))
174
+ }
175
+
176
+ console.log('')
177
+ })
178
+
179
+ const answer = await askQuestion('Elige un número (1-3) o \'q\' para salir: ')
180
+
181
+ if (answer.toLowerCase() === 'q') {
182
+ console.log(chalk.dim('\nHecho con <3 por icarus.mx'))
183
+ rl.close()
184
+ return
185
+ }
186
+
187
+ const selectedIndex = parseInt(answer) - 1
188
+
189
+ if (selectedIndex >= 0 && selectedIndex < papers.length) {
190
+ const selectedPaper = papers[selectedIndex]
191
+
192
+ if (!selectedPaper.unlocked) {
193
+ console.log(chalk.yellow(`\n🔒 Este paper está bloqueado\n`))
194
+ console.log(chalk.dim(`Requisito: ${selectedPaper.requirement}\n`))
195
+ rl.close()
196
+ return
197
+ }
198
+
199
+ rl.close()
200
+ await this.launchPaper(selectedPaper.id)
201
+ } else {
202
+ console.log(chalk.red('❌ Opción inválida'))
203
+ rl.close()
204
+ }
205
+
206
+ } catch (error) {
207
+ console.error('Error:', error.message)
208
+ rl.close()
209
+ }
210
+ }
211
+
212
+ async launchPaper(paperId) {
213
+ process.stdout.write('\x1b[2J')
214
+ process.stdout.write('\x1b[H')
215
+
216
+ // Get paper markdown path
217
+ const paperPath = path.join(__dirname, paperId, `${paperId}.md`)
218
+
219
+ // Show intro message
220
+ const papers = this.accessControl.getPaperRequirements()
221
+ const paper = papers[paperId]
222
+
223
+ console.log(chalk.bold.cyan(`\n📄 ${paper.name}`))
224
+ console.log(chalk.dim(paper.description))
225
+ console.log(chalk.dim('─'.repeat(60)))
226
+ console.log('')
227
+ console.log(chalk.yellow('💡 Vim Tips para empezar:'))
228
+ console.log(chalk.white(' zM - Cierra todos los folds (empieza aquí)'))
229
+ console.log(chalk.white(' za - Abre/cierra fold bajo el cursor'))
230
+ console.log(chalk.white(' } - Salta al siguiente párrafo'))
231
+ console.log(chalk.white(' ]] - Salta a la siguiente sección'))
232
+ console.log('')
233
+ console.log(chalk.green('Presiona Enter para abrir en Neovim...'))
234
+ console.log(chalk.dim('(El paper se marcará completo al cerrar Neovim)'))
235
+
236
+ const rl = createInterface({
237
+ input: process.stdin,
238
+ output: process.stdout
239
+ })
240
+
241
+ await new Promise(resolve => {
242
+ rl.question('', () => {
243
+ rl.close()
244
+ resolve()
245
+ })
246
+ })
247
+
248
+ // Launch nvim
249
+ console.log(chalk.dim('\nAbriendo Neovim...\n'))
250
+
251
+ const result = spawnSync('nvim', ['+zM', paperPath], {
252
+ stdio: 'inherit',
253
+ shell: true
254
+ })
255
+
256
+ // Mark as completed after closing nvim
257
+ if (result.status === 0 || result.status === null) {
258
+ this.accessControl.markPaperCompleted(paperId)
259
+
260
+ console.log('')
261
+ console.log(chalk.bold.green('🎉 ¡Paper completado!'))
262
+ console.log(chalk.dim('Tu progreso ha sido guardado en ~/.creta/user.json'))
263
+ console.log('')
264
+
265
+ // Check what's unlocked now
266
+ const availablePapers = this.accessControl.getAvailablePapers()
267
+ const nextPaper = availablePapers.find(p => !p.completed && p.unlocked)
268
+
269
+ if (nextPaper) {
270
+ console.log(chalk.cyan(`✨ Desbloqueaste: ${nextPaper.name}`))
271
+ console.log(chalk.dim(` ${nextPaper.description}`))
272
+ console.log('')
273
+ }
274
+ }
275
+ }
276
+ }
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "@icarusmx/creta",
3
- "version": "1.4.18",
3
+ "version": "1.5.1",
4
4
  "description": "Salgamos de este laberinto.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "creta": "./bin/creta.js"
8
8
  },
9
- "scripts": {
10
- "test": "node --test"
11
- },
12
9
  "keywords": [
13
10
  "cli",
14
11
  "icarus",