@icarusmx/creta 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,8 @@
3
3
  "allow": [
4
4
  "Read(//private/tmp/**)",
5
5
  "Bash(node:*)",
6
- "WebFetch(domain:github.com)"
6
+ "WebFetch(domain:github.com)",
7
+ "Bash(npm install)"
7
8
  ],
8
9
  "deny": [],
9
10
  "ask": []
package/bin/creta.js CHANGED
@@ -5,11 +5,12 @@ import { execSync } from 'child_process'
5
5
  import fs from 'fs'
6
6
  import path from 'path'
7
7
  import { fileURLToPath } from 'url'
8
+ import { CretaCodeSession } from '../lib/session.js'
8
9
 
9
10
  const args = process.argv.slice(2)
10
11
  const command = args[0]
11
12
 
12
- console.log("¡Bienvenido de vuelta! 🏛️")
13
+ console.log("¡Bienvenida de vuelta! 🏛️")
13
14
  console.log("Retomemos desde donde nos quedamos: hagamos tu portafolio.\n")
14
15
 
15
16
  if (!command) {
@@ -18,6 +19,7 @@ if (!command) {
18
19
  console.log(" creta portafolio-1 - Desbloquea nivel 1 (navbar) 🔓")
19
20
  console.log(" creta portafolio-2 - Desbloquea nivel 2 (navbar + hero) 🔓")
20
21
  console.log(" creta portafolio-3 - Desbloquea nivel 3 (solución completa) 🔓")
22
+ console.log(" creta code - Inicia sesión interactiva de programación 🤖")
21
23
  console.log("\n💡 Tip: Si estás dentro de un proyecto existente, los comandos")
22
24
  console.log(" portafolio-1/2/3 actualizarán tus archivos directamente")
23
25
  process.exit(0)
@@ -32,6 +34,10 @@ if (command.startsWith('portafolio')) {
32
34
  } else {
33
35
  await createPortfolioProject(level)
34
36
  }
37
+ } else if (command === 'code') {
38
+ // Start interactive coding session
39
+ const session = new CretaCodeSession()
40
+ await session.start()
35
41
  } else {
36
42
  console.log(`Comando no reconocido: ${command}`)
37
43
  process.exit(1)
@@ -199,22 +205,205 @@ function applyPageModifications(content, level) {
199
205
  </p>
200
206
  </div>
201
207
  </section>`,
202
- `<section class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
208
+ `<section id="inicio" class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
203
209
  <div class="max-w-4xl mx-auto px-4 text-center">
204
- <h1 class="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
205
- Hola, soy {{STUDENT_NAME}}
206
- </h1>
207
- <p class="text-lg text-gray-600 mb-8 max-w-2xl mx-auto">
208
- Soy un desarrollador apasionado por crear productos digitales que impacten positivamente.
209
- Aprendo construyendo y siempre busco nuevos desafíos.
210
- </p>
211
- <div class="flex justify-center space-x-4">
212
- <a href="#proyectos" class="bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-lg transition-colors">
213
- Ver proyectos
214
- </a>
215
- <a href="#contacto" class="border border-blue-600 text-blue-600 hover:bg-blue-50 px-8 py-3 rounded-lg transition-colors">
216
- Contáctame
217
- </a>
210
+ <div class="animate-fade-in">
211
+ <h1 class="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
212
+ Hola, soy {{STUDENT_NAME}}
213
+ </h1>
214
+ <p class="text-lg text-gray-600 mb-8 max-w-2xl mx-auto">
215
+ Soy un desarrollador apasionado por crear productos digitales que impacten positivamente.
216
+ Aprendo construyendo y siempre busco nuevos desafíos.
217
+ </p>
218
+ <div class="flex justify-center space-x-4">
219
+ <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">
220
+ Ver proyectos
221
+ </a>
222
+ <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">
223
+ Contáctame
224
+ </a>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </section>`
229
+ )
230
+ }
231
+
232
+ if (level >= 3) {
233
+ // Add complete sections for level 3
234
+ content = content.replace(
235
+ `<!-- ⚠️ AQUÍ VAN LAS DEMÁS SECCIONES (SOBRE MÍ, PROYECTOS, CONTACTO) -->
236
+ <!-- Revisa los comentarios arriba para saber qué crear -->`,
237
+ `<!-- Sobre mí -->
238
+ <section id="sobre-mi" class="py-20 bg-white">
239
+ <div class="max-w-4xl mx-auto px-4">
240
+ <div class="text-center mb-16">
241
+ <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Sobre mí</h2>
242
+ <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
243
+ </div>
244
+
245
+ <div class="grid md:grid-cols-2 gap-12 items-center">
246
+ <div>
247
+ <div class="bg-gray-100 aspect-square rounded-lg flex items-center justify-center mb-6 md:mb-0">
248
+ <span class="text-gray-500 text-lg">Tu foto aquí</span>
249
+ </div>
250
+ </div>
251
+ <div>
252
+ <p class="text-lg text-gray-600 mb-6 leading-relaxed">
253
+ Soy un desarrollador apasionado por crear productos digitales que impacten positivamente.
254
+ Aprendo construyendo y siempre busco nuevos desafíos que me permitan crecer profesionalmente.
255
+ </p>
256
+ <div class="grid grid-cols-2 gap-4">
257
+ <div class="bg-blue-50 p-4 rounded-lg text-center">
258
+ <div class="text-2xl font-bold text-blue-600">2+</div>
259
+ <div class="text-sm text-gray-600">Años aprendiendo</div>
260
+ </div>
261
+ <div class="bg-green-50 p-4 rounded-lg text-center">
262
+ <div class="text-2xl font-bold text-green-600">5+</div>
263
+ <div class="text-sm text-gray-600">Proyectos</div>
264
+ </div>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ </div>
269
+ </section>
270
+
271
+ <!-- Proyectos -->
272
+ <section id="proyectos" class="py-20 bg-gray-50">
273
+ <div class="max-w-6xl mx-auto px-4">
274
+ <div class="text-center mb-16">
275
+ <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Mis Proyectos</h2>
276
+ <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
277
+ <p class="text-lg text-gray-600 max-w-2xl mx-auto">
278
+ Algunos de los proyectos en los que he trabajado y que demuestran mis habilidades
279
+ </p>
280
+ </div>
281
+
282
+ <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
283
+ <!-- Proyecto 1 -->
284
+ <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
285
+ <div class="bg-gradient-to-br from-blue-400 to-blue-600 h-48 flex items-center justify-center">
286
+ <span class="text-white text-lg font-medium">Proyecto 1</span>
287
+ </div>
288
+ <div class="p-6">
289
+ <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
290
+ <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
291
+ <div class="flex space-x-3">
292
+ <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
293
+ <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
294
+ </div>
295
+ </div>
296
+ </div>
297
+
298
+ <!-- Proyecto 2 -->
299
+ <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
300
+ <div class="bg-gradient-to-br from-green-400 to-green-600 h-48 flex items-center justify-center">
301
+ <span class="text-white text-lg font-medium">Proyecto 2</span>
302
+ </div>
303
+ <div class="p-6">
304
+ <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
305
+ <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
306
+ <div class="flex space-x-3">
307
+ <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
308
+ <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
309
+ </div>
310
+ </div>
311
+ </div>
312
+
313
+ <!-- Proyecto 3 -->
314
+ <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
315
+ <div class="bg-gradient-to-br from-purple-400 to-purple-600 h-48 flex items-center justify-center">
316
+ <span class="text-white text-lg font-medium">Proyecto 3</span>
317
+ </div>
318
+ <div class="p-6">
319
+ <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
320
+ <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
321
+ <div class="flex space-x-3">
322
+ <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
323
+ <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
324
+ </div>
325
+ </div>
326
+ </div>
327
+ </div>
328
+ </div>
329
+ </section>
330
+
331
+ <!-- Contacto -->
332
+ <section id="contacto" class="py-20 bg-white">
333
+ <div class="max-w-4xl mx-auto px-4">
334
+ <div class="text-center mb-16">
335
+ <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Contacto</h2>
336
+ <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
337
+ <p class="text-lg text-gray-600 max-w-2xl mx-auto">
338
+ ¿Tienes un proyecto en mente? ¡Hablemos y veamos cómo puedo ayudarte!
339
+ </p>
340
+ </div>
341
+
342
+ <div class="grid md:grid-cols-2 gap-12">
343
+ <div>
344
+ <h3 class="text-xl font-semibold text-gray-900 mb-6">Información de contacto</h3>
345
+ <div class="space-y-4">
346
+ <div class="flex items-center space-x-3">
347
+ <div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
348
+ <svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
349
+ <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>
350
+ </svg>
351
+ </div>
352
+ <div>
353
+ <p class="text-gray-900 font-medium">Email</p>
354
+ <p class="text-gray-600">tu.email@ejemplo.com</p>
355
+ </div>
356
+ </div>
357
+
358
+ <div class="flex items-center space-x-3">
359
+ <div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
360
+ <svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
361
+ <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>
362
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
363
+ </svg>
364
+ </div>
365
+ <div>
366
+ <p class="text-gray-900 font-medium">Ubicación</p>
367
+ <p class="text-gray-600">Tu ciudad, País</p>
368
+ </div>
369
+ </div>
370
+ </div>
371
+
372
+ <div class="mt-8">
373
+ <h4 class="text-lg font-medium text-gray-900 mb-4">Sígueme</h4>
374
+ <div class="flex space-x-4">
375
+ <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">
376
+ <span class="text-gray-600 hover:text-blue-600">LI</span>
377
+ </a>
378
+ <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">
379
+ <span class="text-gray-600 hover:text-blue-600">GH</span>
380
+ </a>
381
+ <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">
382
+ <span class="text-gray-600 hover:text-blue-600">TW</span>
383
+ </a>
384
+ </div>
385
+ </div>
386
+ </div>
387
+
388
+ <div>
389
+ <form class="space-y-6">
390
+ <div>
391
+ <label for="name" class="block text-sm font-medium text-gray-700 mb-2">Nombre</label>
392
+ <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">
393
+ </div>
394
+ <div>
395
+ <label for="email" class="block text-sm font-medium text-gray-700 mb-2">Email</label>
396
+ <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">
397
+ </div>
398
+ <div>
399
+ <label for="message" class="block text-sm font-medium text-gray-700 mb-2">Mensaje</label>
400
+ <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>
401
+ </div>
402
+ <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">
403
+ Enviar mensaje
404
+ </button>
405
+ </form>
406
+ </div>
218
407
  </div>
219
408
  </div>
220
409
  </section>`
@@ -0,0 +1,41 @@
1
+ export class BaseLLMProvider {
2
+ constructor(config = {}) {
3
+ this.config = config
4
+ this.name = 'base'
5
+ }
6
+
7
+ async chat(messages, options = {}) {
8
+ throw new Error('chat method must be implemented by provider')
9
+ }
10
+
11
+ async isAvailable() {
12
+ return false
13
+ }
14
+
15
+ formatMessages(userInput, context = {}) {
16
+ const systemPrompt = `Eres un mentor de programación que ayuda a estudiantes mexicanos a aprender desarrollo web.
17
+
18
+ CONTEXTO:
19
+ - Eres parte de Creta, una escuela de software mexicana
20
+ - Los estudiantes están aprendiendo SvelteKit 5 + Tailwind CSS 4
21
+ - Usa un tono amigable y alentador en español
22
+ - Enfócate en explicaciones prácticas y ejemplos de código
23
+ - Menciona conceptos de programación cuando sea relevante
24
+
25
+ PROYECTO ACTUAL:
26
+ ${context.currentProject ? `- Proyecto: ${context.currentProject.name}` : '- No hay proyecto activo'}
27
+ ${context.currentFile ? `- Archivo actual: ${context.currentFile}` : ''}
28
+
29
+ INSTRUCCIONES:
30
+ - Responde en español mexicano
31
+ - Usa emojis ocasionalmente para hacer más amigable la conversación
32
+ - Si es una pregunta técnica, incluye código de ejemplo
33
+ - Si mencionas Tailwind, usa clases actualizadas de la v4
34
+ - Mantén las respuestas concisas pero útiles`
35
+
36
+ return [
37
+ { role: 'system', content: systemPrompt },
38
+ { role: 'user', content: userInput }
39
+ ]
40
+ }
41
+ }
@@ -0,0 +1,50 @@
1
+ import fetch from 'node-fetch'
2
+ import { BaseLLMProvider } from './base.js'
3
+
4
+ export class GroqProvider extends BaseLLMProvider {
5
+ constructor(config = {}) {
6
+ super(config)
7
+ this.name = 'groq'
8
+ this.apiKey = config.apiKey || process.env.GROQ_API_KEY
9
+ this.model = config.model || 'mixtral-8x7b-32768'
10
+ this.baseURL = 'https://api.groq.com/openai/v1'
11
+ }
12
+
13
+ async isAvailable() {
14
+ return !!this.apiKey
15
+ }
16
+
17
+ async chat(userInput, context = {}) {
18
+ if (!this.apiKey) {
19
+ throw new Error('API key de Groq no configurada. Usa: export GROQ_API_KEY=tu_clave')
20
+ }
21
+
22
+ const messages = this.formatMessages(userInput, context)
23
+
24
+ try {
25
+ const response = await fetch(`${this.baseURL}/chat/completions`, {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Authorization': `Bearer ${this.apiKey}`,
29
+ 'Content-Type': 'application/json'
30
+ },
31
+ body: JSON.stringify({
32
+ model: this.model,
33
+ messages,
34
+ max_tokens: 1000,
35
+ temperature: 0.7
36
+ })
37
+ })
38
+
39
+ if (!response.ok) {
40
+ throw new Error(`Groq API error: ${response.status}`)
41
+ }
42
+
43
+ const data = await response.json()
44
+ return data.choices[0].message.content
45
+
46
+ } catch (error) {
47
+ throw new Error(`Error conectando con Groq: ${error.message}`)
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,66 @@
1
+ import { OpenAIProvider } from './openai.js'
2
+ import { GroqProvider } from './groq.js'
3
+
4
+ export class LLMManager {
5
+ constructor() {
6
+ this.providers = new Map()
7
+ this.currentProvider = null
8
+
9
+ // Register available providers
10
+ this.registerProvider('openai', OpenAIProvider)
11
+ this.registerProvider('groq', GroqProvider)
12
+ }
13
+
14
+ registerProvider(name, ProviderClass) {
15
+ this.providers.set(name, ProviderClass)
16
+ }
17
+
18
+ async getAvailableProviders() {
19
+ const available = []
20
+
21
+ for (const [name, ProviderClass] of this.providers) {
22
+ const provider = new ProviderClass()
23
+ if (await provider.isAvailable()) {
24
+ available.push(name)
25
+ }
26
+ }
27
+
28
+ return available
29
+ }
30
+
31
+ async selectProvider(preferredProvider = null) {
32
+ const available = await this.getAvailableProviders()
33
+
34
+ if (available.length === 0) {
35
+ throw new Error('No hay proveedores de IA disponibles. Configura una API key:\n' +
36
+ '- OpenAI: export OPENAI_API_KEY=tu_clave\n' +
37
+ '- Groq: export GROQ_API_KEY=tu_clave')
38
+ }
39
+
40
+ // Use preferred provider if available
41
+ if (preferredProvider && available.includes(preferredProvider)) {
42
+ const ProviderClass = this.providers.get(preferredProvider)
43
+ this.currentProvider = new ProviderClass()
44
+ return preferredProvider
45
+ }
46
+
47
+ // Otherwise use first available
48
+ const selectedName = available[0]
49
+ const ProviderClass = this.providers.get(selectedName)
50
+ this.currentProvider = new ProviderClass()
51
+
52
+ return selectedName
53
+ }
54
+
55
+ async chat(userInput, context = {}) {
56
+ if (!this.currentProvider) {
57
+ await this.selectProvider()
58
+ }
59
+
60
+ return await this.currentProvider.chat(userInput, context)
61
+ }
62
+
63
+ getCurrentProviderName() {
64
+ return this.currentProvider?.name || 'ninguno'
65
+ }
66
+ }
@@ -0,0 +1,50 @@
1
+ import fetch from 'node-fetch'
2
+ import { BaseLLMProvider } from './base.js'
3
+
4
+ export class OpenAIProvider extends BaseLLMProvider {
5
+ constructor(config = {}) {
6
+ super(config)
7
+ this.name = 'openai'
8
+ this.apiKey = config.apiKey || process.env.OPENAI_API_KEY
9
+ this.model = config.model || 'gpt-4o-mini'
10
+ this.baseURL = config.baseURL || 'https://api.openai.com/v1'
11
+ }
12
+
13
+ async isAvailable() {
14
+ return !!this.apiKey
15
+ }
16
+
17
+ async chat(userInput, context = {}) {
18
+ if (!this.apiKey) {
19
+ throw new Error('API key de OpenAI no configurada. Usa: export OPENAI_API_KEY=tu_clave')
20
+ }
21
+
22
+ const messages = this.formatMessages(userInput, context)
23
+
24
+ try {
25
+ const response = await fetch(`${this.baseURL}/chat/completions`, {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Authorization': `Bearer ${this.apiKey}`,
29
+ 'Content-Type': 'application/json'
30
+ },
31
+ body: JSON.stringify({
32
+ model: this.model,
33
+ messages,
34
+ max_tokens: 1000,
35
+ temperature: 0.7
36
+ })
37
+ })
38
+
39
+ if (!response.ok) {
40
+ throw new Error(`OpenAI API error: ${response.status}`)
41
+ }
42
+
43
+ const data = await response.json()
44
+ return data.choices[0].message.content
45
+
46
+ } catch (error) {
47
+ throw new Error(`Error conectando con OpenAI: ${error.message}`)
48
+ }
49
+ }
50
+ }
package/lib/session.js ADDED
@@ -0,0 +1,437 @@
1
+ import { createInterface } from 'readline'
2
+ import chalk from 'chalk'
3
+ import chokidar from 'chokidar'
4
+ import fs from 'fs'
5
+ import path from 'path'
6
+ import { LLMManager } from './providers/index.js'
7
+
8
+ export class CretaCodeSession {
9
+ constructor() {
10
+ this.rl = null
11
+ this.watcher = null
12
+ this.currentProject = null
13
+ this.isActive = false
14
+ this.llm = new LLMManager()
15
+ this.context = {
16
+ files: new Map(),
17
+ lastModified: new Map()
18
+ }
19
+ }
20
+
21
+ async start() {
22
+ console.log(chalk.cyan('🏛️ Creta Code - Sesión Interactiva'))
23
+ console.log(chalk.gray('Escribe "ayuda" para ver comandos disponibles\n'))
24
+
25
+ // Initialize project context
26
+ await this.initializeContext()
27
+
28
+ // Initialize LLM provider
29
+ await this.initializeLLM()
30
+
31
+ // Setup file watcher
32
+ this.setupFileWatcher()
33
+
34
+ // Setup readline interface
35
+ this.rl = createInterface({
36
+ input: process.stdin,
37
+ output: process.stdout,
38
+ prompt: chalk.blue('creta> ')
39
+ })
40
+
41
+ this.isActive = true
42
+ this.rl.prompt()
43
+
44
+ this.rl.on('line', async (input) => {
45
+ await this.handleCommand(input.trim())
46
+ if (this.isActive) {
47
+ this.rl.prompt()
48
+ }
49
+ })
50
+
51
+ this.rl.on('close', () => {
52
+ this.cleanup()
53
+ })
54
+ }
55
+
56
+ async initializeContext() {
57
+ const cwd = process.cwd()
58
+
59
+ // Check if we're in a Creta project
60
+ if (this.isInCretaProject(cwd)) {
61
+ this.currentProject = {
62
+ path: cwd,
63
+ type: 'creta-portfolio',
64
+ name: this.extractProjectName(cwd)
65
+ }
66
+ console.log(chalk.green(`📁 Proyecto detectado: ${this.currentProject.name}`))
67
+ } else {
68
+ console.log(chalk.yellow('⚠️ No se detectó un proyecto Creta. Algunas funciones podrían estar limitadas.'))
69
+ }
70
+ }
71
+
72
+ async initializeLLM() {
73
+ try {
74
+ const provider = await this.llm.selectProvider()
75
+ console.log(chalk.green(`🤖 IA activada: ${provider}`))
76
+ } catch (error) {
77
+ console.log(chalk.yellow('⚠️ IA no disponible: ' + error.message))
78
+ console.log(chalk.gray(' Algunas funciones estarán limitadas\n'))
79
+ }
80
+ }
81
+
82
+ setupFileWatcher() {
83
+ if (!this.currentProject) return
84
+
85
+ const watchPaths = [
86
+ path.join(this.currentProject.path, 'src/**/*.svelte'),
87
+ path.join(this.currentProject.path, 'src/**/*.js'),
88
+ path.join(this.currentProject.path, 'src/**/*.css')
89
+ ]
90
+
91
+ this.watcher = chokidar.watch(watchPaths, {
92
+ ignored: /node_modules/,
93
+ persistent: true
94
+ })
95
+
96
+ this.watcher.on('change', (filePath) => {
97
+ const relativePath = path.relative(this.currentProject.path, filePath)
98
+ console.log(chalk.gray(`\n📝 Archivo modificado: ${relativePath}`))
99
+ this.rl.prompt()
100
+ })
101
+ }
102
+
103
+ async handleCommand(input) {
104
+ const [command, ...args] = input.split(' ')
105
+
106
+ switch (command.toLowerCase()) {
107
+ case 'ayuda':
108
+ case 'help':
109
+ this.showHelp()
110
+ break
111
+
112
+ case 'explica':
113
+ await this.explainCode(args.join(' '))
114
+ break
115
+
116
+ case 'revisa':
117
+ await this.reviewCode(args.join(' '))
118
+ break
119
+
120
+ case 'depura':
121
+ await this.debugCode(args.join(' '))
122
+ break
123
+
124
+ case 'nivel':
125
+ await this.checkLevel()
126
+ break
127
+
128
+ case 'archivo':
129
+ await this.showFileContent(args[0])
130
+ break
131
+
132
+ case 'estado':
133
+ this.showProjectStatus()
134
+ break
135
+
136
+ case 'salir':
137
+ case 'exit':
138
+ console.log(chalk.cyan('\n¡Hasta la vista! 👋'))
139
+ this.isActive = false
140
+ this.rl.close()
141
+ break
142
+
143
+ default:
144
+ if (input) {
145
+ console.log(chalk.red(`Comando no reconocido: "${command}"`))
146
+ console.log(chalk.gray('Escribe "ayuda" para ver comandos disponibles'))
147
+ }
148
+ }
149
+ }
150
+
151
+ showHelp() {
152
+ console.log(chalk.cyan('\n🎯 Comandos disponibles:'))
153
+ console.log(' ' + chalk.blue('explica [componente]') + ' - Explica un archivo o concepto')
154
+ console.log(' ' + chalk.blue('revisa [archivo]') + ' - Revisa tu código y da sugerencias')
155
+ console.log(' ' + chalk.blue('depura [error]') + ' - Ayuda a resolver errores')
156
+ console.log(' ' + chalk.blue('nivel') + ' - Verifica tu progreso actual')
157
+ console.log(' ' + chalk.blue('archivo [nombre]') + ' - Muestra el contenido de un archivo')
158
+ console.log(' ' + chalk.blue('estado') + ' - Estado del proyecto actual')
159
+ console.log(' ' + chalk.blue('ayuda') + ' - Muestra esta ayuda')
160
+ console.log(' ' + chalk.blue('salir') + ' - Termina la sesión')
161
+ console.log('')
162
+ }
163
+
164
+ async explainCode(target) {
165
+ if (!target) {
166
+ console.log(chalk.yellow('💡 Uso: explica [archivo o concepto]'))
167
+ console.log(chalk.gray('Ejemplo: explica +layout.svelte'))
168
+ return
169
+ }
170
+
171
+ console.log(chalk.cyan(`🔍 Explicando: ${target}`))
172
+
173
+ try {
174
+ let context = {
175
+ currentProject: this.currentProject,
176
+ currentFile: target
177
+ }
178
+
179
+ // If it's a file, try to read its content
180
+ const filePath = this.findFile(target)
181
+ if (filePath && fs.existsSync(filePath)) {
182
+ const content = fs.readFileSync(filePath, 'utf8')
183
+ context.fileContent = content.slice(0, 2000) // Limit content for API
184
+ }
185
+
186
+ const prompt = filePath
187
+ ? `Explica este archivo: ${target}\n\nContenido:\n${context.fileContent || 'No se pudo leer el archivo'}`
188
+ : `Explica este concepto de programación web: ${target}`
189
+
190
+ const response = await this.llm.chat(prompt, context)
191
+ console.log(chalk.green('\n🤖 Explicación:'))
192
+ console.log(response)
193
+
194
+ } catch (error) {
195
+ console.log(chalk.red(`❌ Error: ${error.message}`))
196
+
197
+ // Fallback explanation
198
+ if (target.includes('.svelte')) {
199
+ console.log(chalk.green('\n📚 Explicación básica:'))
200
+ console.log('Este es un componente Svelte que define la estructura de tu página.')
201
+ console.log('- Los archivos +layout.svelte definen el layout común (navbar, footer)')
202
+ console.log('- Los archivos +page.svelte definen el contenido específico de cada página')
203
+ console.log('- Tailwind CSS se usa para el styling con clases como "bg-blue-600"')
204
+ }
205
+ }
206
+ }
207
+
208
+ async reviewCode(fileName) {
209
+ if (!this.currentProject) {
210
+ console.log(chalk.red('❌ No hay proyecto activo para revisar'))
211
+ return
212
+ }
213
+
214
+ if (!fileName) {
215
+ console.log(chalk.yellow('💡 Uso: revisa [archivo]'))
216
+ console.log(chalk.gray('Ejemplo: revisa +layout.svelte'))
217
+ return
218
+ }
219
+
220
+ const filePath = this.findFile(fileName)
221
+ if (!filePath) {
222
+ console.log(chalk.red(`❌ No se encontró el archivo: ${fileName}`))
223
+ return
224
+ }
225
+
226
+ console.log(chalk.cyan(`🔍 Revisando: ${fileName}`))
227
+
228
+ try {
229
+ const content = fs.readFileSync(filePath, 'utf8')
230
+
231
+ const context = {
232
+ currentProject: this.currentProject,
233
+ currentFile: fileName,
234
+ fileContent: content.slice(0, 3000)
235
+ }
236
+
237
+ const prompt = `Revisa este código y dame sugerencias de mejora. Enfócate en:
238
+ - Buenas prácticas de SvelteKit 5
239
+ - Uso correcto de Tailwind CSS 4
240
+ - Estructura y organización del código
241
+ - Posibles errores o mejoras
242
+
243
+ Archivo: ${fileName}
244
+
245
+ Código:
246
+ ${context.fileContent}`
247
+
248
+ const response = await this.llm.chat(prompt, context)
249
+ console.log(chalk.green('\n🤖 Revisión de código:'))
250
+ console.log(response)
251
+
252
+ } catch (error) {
253
+ console.log(chalk.red(`❌ Error: ${error.message}`))
254
+
255
+ // Fallback basic review
256
+ console.log(chalk.green('\n✅ Revisión básica:'))
257
+
258
+ if (content.includes('⚠️')) {
259
+ console.log(chalk.yellow('- Hay comentarios con retos pendientes (⚠️)'))
260
+ }
261
+
262
+ if (content.includes('TODO') || content.includes('FIXME')) {
263
+ console.log(chalk.yellow('- Encontré comentarios TODO/FIXME'))
264
+ }
265
+
266
+ console.log(chalk.green('- La sintaxis parece correcta'))
267
+ }
268
+ }
269
+
270
+ async debugCode(issue) {
271
+ console.log(chalk.cyan(`🐛 Depurando: ${issue || 'proyecto actual'}`))
272
+
273
+ if (!this.currentProject) {
274
+ console.log(chalk.yellow('💡 Tips generales:'))
275
+ console.log('- Asegúrate de estar en un proyecto Creta')
276
+ console.log('- Ejecuta "npm install" para instalar dependencias')
277
+ console.log('- Usa "npm run dev" para iniciar el servidor')
278
+ return
279
+ }
280
+
281
+ console.log(chalk.green('\n🔧 Verificaciones:'))
282
+
283
+ // Check package.json
284
+ const packagePath = path.join(this.currentProject.path, 'package.json')
285
+ if (fs.existsSync(packagePath)) {
286
+ console.log(chalk.green('✅ package.json existe'))
287
+ }
288
+
289
+ // Check node_modules
290
+ const nodeModulesPath = path.join(this.currentProject.path, 'node_modules')
291
+ if (fs.existsSync(nodeModulesPath)) {
292
+ console.log(chalk.green('✅ Dependencias instaladas'))
293
+ } else {
294
+ console.log(chalk.yellow('⚠️ Ejecuta: npm install'))
295
+ }
296
+
297
+ console.log(chalk.gray('\n(Próximamente: diagnóstico avanzado con IA)'))
298
+ }
299
+
300
+ async checkLevel() {
301
+ if (!this.currentProject) {
302
+ console.log(chalk.red('❌ No hay proyecto activo'))
303
+ return
304
+ }
305
+
306
+ console.log(chalk.cyan('📊 Verificando progreso...'))
307
+
308
+ const layoutPath = path.join(this.currentProject.path, 'src/routes/+layout.svelte')
309
+ const pagePath = path.join(this.currentProject.path, 'src/routes/+page.svelte')
310
+
311
+ let level = 0
312
+
313
+ if (fs.existsSync(layoutPath)) {
314
+ const layoutContent = fs.readFileSync(layoutPath, 'utf8')
315
+ if (!layoutContent.includes('⚠️ COMPLETA AQUÍ LA NAVEGACIÓN')) {
316
+ level = Math.max(level, 1)
317
+ }
318
+ if (!layoutContent.includes('⚠️ COMPLETA AQUÍ EL FOOTER')) {
319
+ level = Math.max(level, 3)
320
+ }
321
+ }
322
+
323
+ if (fs.existsSync(pagePath)) {
324
+ const pageContent = fs.readFileSync(pagePath, 'utf8')
325
+ if (!pageContent.includes('⚠️ COMPLETA AQUÍ LA SECCIÓN HERO')) {
326
+ level = Math.max(level, 2)
327
+ }
328
+ }
329
+
330
+ console.log(chalk.green(`\n🎯 Nivel actual: ${level}`))
331
+ if (level === 0) console.log(chalk.gray('Completa el navbar para llegar al nivel 1'))
332
+ if (level === 1) console.log(chalk.gray('Completa la sección hero para llegar al nivel 2'))
333
+ if (level === 2) console.log(chalk.gray('Completa el footer para llegar al nivel 3'))
334
+ if (level === 3) console.log(chalk.green('¡Portafolio completo! 🎉'))
335
+ }
336
+
337
+ async showFileContent(fileName) {
338
+ if (!fileName) {
339
+ console.log(chalk.yellow('💡 Uso: archivo [nombre]'))
340
+ return
341
+ }
342
+
343
+ const filePath = this.findFile(fileName)
344
+ if (!filePath) {
345
+ console.log(chalk.red(`❌ No se encontró: ${fileName}`))
346
+ return
347
+ }
348
+
349
+ try {
350
+ const content = fs.readFileSync(filePath, 'utf8')
351
+ const lines = content.split('\n')
352
+
353
+ console.log(chalk.cyan(`\n📄 ${fileName}:`))
354
+ console.log(chalk.gray('─'.repeat(50)))
355
+
356
+ lines.slice(0, 20).forEach((line, i) => {
357
+ console.log(chalk.gray(`${(i + 1).toString().padStart(2)}: `) + line)
358
+ })
359
+
360
+ if (lines.length > 20) {
361
+ console.log(chalk.gray(`... y ${lines.length - 20} líneas más`))
362
+ }
363
+
364
+ } catch (error) {
365
+ console.log(chalk.red(`❌ Error: ${error.message}`))
366
+ }
367
+ }
368
+
369
+ showProjectStatus() {
370
+ console.log(chalk.cyan('\n📊 Estado del Proyecto:'))
371
+
372
+ if (this.currentProject) {
373
+ console.log(chalk.green(`✅ Proyecto: ${this.currentProject.name}`))
374
+ console.log(chalk.green(`📁 Ruta: ${this.currentProject.path}`))
375
+ console.log(chalk.green(`🎯 Tipo: ${this.currentProject.type}`))
376
+ } else {
377
+ console.log(chalk.red('❌ No hay proyecto activo'))
378
+ }
379
+
380
+ if (this.watcher) {
381
+ console.log(chalk.green('👀 Monitoreo de archivos: activo'))
382
+ }
383
+ }
384
+
385
+ findFile(fileName) {
386
+ if (!this.currentProject) return null
387
+
388
+ const possiblePaths = [
389
+ path.join(this.currentProject.path, fileName),
390
+ path.join(this.currentProject.path, 'src', fileName),
391
+ path.join(this.currentProject.path, 'src/routes', fileName),
392
+ path.join(this.currentProject.path, 'src/lib', fileName)
393
+ ]
394
+
395
+ for (const filePath of possiblePaths) {
396
+ if (fs.existsSync(filePath)) {
397
+ return filePath
398
+ }
399
+ }
400
+
401
+ return null
402
+ }
403
+
404
+ isInCretaProject(dir) {
405
+ const packageJsonPath = path.join(dir, 'package.json')
406
+ const layoutPath = path.join(dir, 'src/routes/+layout.svelte')
407
+
408
+ if (!fs.existsSync(packageJsonPath) || !fs.existsSync(layoutPath)) {
409
+ return false
410
+ }
411
+
412
+ try {
413
+ const layoutContent = fs.readFileSync(layoutPath, 'utf8')
414
+ return layoutContent.includes('RETO CRETA') || layoutContent.includes('🧭 RETO')
415
+ } catch {
416
+ return false
417
+ }
418
+ }
419
+
420
+ extractProjectName(projectPath) {
421
+ try {
422
+ const packageJsonPath = path.join(projectPath, 'package.json')
423
+ const packageContent = fs.readFileSync(packageJsonPath, 'utf8')
424
+ const packageJson = JSON.parse(packageContent)
425
+ return packageJson.name || path.basename(projectPath)
426
+ } catch {
427
+ return path.basename(projectPath)
428
+ }
429
+ }
430
+
431
+ cleanup() {
432
+ if (this.watcher) {
433
+ this.watcher.close()
434
+ }
435
+ this.isActive = false
436
+ }
437
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icarusmx/creta",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Salgamos de este laberinto.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,5 +9,10 @@
9
9
  "keywords": ["cli", "icarus", "creta", "scythe", "software school"],
10
10
  "author": "Guillermo Rodríguez López",
11
11
  "publishConfig": { "access": "public" },
12
- "license": "MIT"
12
+ "license": "MIT",
13
+ "dependencies": {
14
+ "chalk": "^5.3.0",
15
+ "chokidar": "^3.5.3",
16
+ "node-fetch": "^3.3.2"
17
+ }
13
18
  }
@@ -12,6 +12,7 @@
12
12
  "@sveltejs/kit": "^2.0.0",
13
13
  "@sveltejs/vite-plugin-svelte": "^4.0.0",
14
14
  "svelte": "^5.0.0",
15
+ "tailwindcss": "^4.0.0",
15
16
  "@tailwindcss/vite": "^4.0.0",
16
17
  "vite": "^5.0.3"
17
18
  },
@@ -1,6 +1,4 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import "tailwindcss";
4
2
 
5
3
  /* Animaciones personalizadas */
6
4
  @keyframes fade-in {
@@ -1,20 +1,46 @@
1
1
  <!--
2
- 🌟 RETO 2: HERO SECTION
3
- ========================
2
+ 🌟 RETO CRETA: PORTAFOLIO PERSONAL
3
+ ==================================
4
4
 
5
- Crea la sección principal de tu portfolio que incluya:
6
- - Tu nombre como título principal
5
+ ¡Bienvenido de vuelta! Es hora de crear tu portafolio personal.
6
+ Este proyecto incluye varios retos que puedes completar paso a paso:
7
+
8
+ 🧭 RETO 1: NAVBAR (archivo +layout.svelte)
9
+ - Crear la navegación principal
10
+ - Incluir tu logo y enlaces a las secciones
11
+
12
+ 🎯 RETO 2: HERO SECTION (esta sección)
13
+ - Crear la sección principal de tu portfolio
14
+ - Incluir tu nombre como título principal
7
15
  - Una descripción breve de quién eres
8
16
  - Un botón de llamada a la acción
9
- - Opcionalmente: tu foto o avatar
10
17
 
11
- TIPS DE TAILWIND:
18
+ 📝 RETO 3: SOBRE MÍ
19
+ - Sección con información personal
20
+ - Tu foto o avatar
21
+ - Habilidades y experiencia
22
+
23
+ 🚀 RETO 4: PROYECTOS
24
+ - Muestra tus mejores proyectos
25
+ - Incluye imágenes, descripciones y enlaces
26
+
27
+ 📧 RETO 5: CONTACTO
28
+ - Formulario de contacto
29
+ - Información de contacto
30
+ - Enlaces a redes sociales
31
+
32
+ 💡 Si te quedas atascado, puedes usar:
33
+ - creta portafolio-1 (para desbloquear navbar)
34
+ - creta portafolio-2 (para desbloquear navbar + hero)
35
+ - creta portafolio-3 (para la solución completa)
36
+
37
+ TIPS DE TAILWIND para la HERO SECTION:
12
38
  - Usa 'min-h-screen flex items-center justify-center' para centrar verticalmente
13
39
  - Usa 'text-4xl md:text-6xl font-bold' para títulos grandes y responsivos
14
40
  - Usa 'text-lg text-gray-600 mb-8' para descripciones
15
41
  - Usa 'bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-lg' para botones
16
42
 
17
- ESTRUCTURA SUGERIDA:
43
+ ESTRUCTURA SUGERIDA PARA HERO:
18
44
  ```html
19
45
  <section class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
20
46
  <div class="max-w-4xl mx-auto px-4 text-center">
@@ -37,199 +63,17 @@
37
63
  <meta name="description" content="Portafolio personal de {{STUDENT_NAME}}" />
38
64
  </svelte:head>
39
65
 
40
- <section id="inicio" class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
66
+ <section class="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
41
67
  <!-- ⚠️ COMPLETA AQUÍ LA SECCIÓN HERO -->
42
68
  <div class="max-w-4xl mx-auto px-4 text-center">
43
- <div class="animate-fade-in">
44
- <h1 class="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
45
- Gracias por seguir aquí
46
- </h1>
47
- <p class="text-lg text-gray-600 mb-8 max-w-2xl mx-auto">
48
- Abre el proyecto usando <code class="bg-gray-200 px-2 py-1 rounded text-sm">code .</code> y sigue las instrucciones. Si tienes dudas, apóyate en el equipo
49
- </p>
50
- <div class="flex justify-center space-x-4">
51
- <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">
52
- Ver proyectos
53
- </a>
54
- <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">
55
- Contáctame
56
- </a>
57
- </div>
58
- </div>
69
+ <h1 class="text-4xl md:text-6xl font-bold text-gray-900 mb-6">
70
+ Gracias por seguir aquí
71
+ </h1>
72
+ <p class="text-lg text-gray-600 mb-8">
73
+ Abre el proyecto usando <code>code .</code> y sigue las instrucciones. Si tienes dudas, apóyate en el equipo
74
+ </p>
59
75
  </div>
60
76
  </section>
61
77
 
62
- <!-- Sobre -->
63
- <section id="sobre-mi" class="py-20 bg-white">
64
- <div class="max-w-4xl mx-auto px-4">
65
- <div class="text-center mb-16">
66
- <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Sobre mí</h2>
67
- <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
68
- </div>
69
-
70
- <div class="grid md:grid-cols-2 gap-12 items-center">
71
- <div>
72
- <div class="bg-gray-100 aspect-square rounded-lg flex items-center justify-center mb-6 md:mb-0">
73
- <span class="text-gray-500 text-lg">Tu foto aquí</span>
74
- </div>
75
- </div>
76
- <div>
77
- <p class="text-lg text-gray-600 mb-6 leading-relaxed">
78
- <!-- ⚠️ PERSONALIZA TU DESCRIPCIÓN AQUÍ -->
79
- Soy un desarrollador apasionado por crear productos digitales que impacten positivamente.
80
- Aprendo construyendo y siempre busco nuevos desafíos que me permitan crecer profesionalmente.
81
- </p>
82
- <div class="grid grid-cols-2 gap-4">
83
- <div class="bg-blue-50 p-4 rounded-lg text-center">
84
- <div class="text-2xl font-bold text-blue-600">2+</div>
85
- <div class="text-sm text-gray-600">Años aprendiendo</div>
86
- </div>
87
- <div class="bg-green-50 p-4 rounded-lg text-center">
88
- <div class="text-2xl font-bold text-green-600">5+</div>
89
- <div class="text-sm text-gray-600">Proyectos</div>
90
- </div>
91
- </div>
92
- </div>
93
- </div>
94
- </div>
95
- </section>
96
-
97
- <!-- Proyectos -->
98
- <section id="proyectos" class="py-20 bg-gray-50">
99
- <div class="max-w-6xl mx-auto px-4">
100
- <div class="text-center mb-16">
101
- <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Mis Proyectos</h2>
102
- <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
103
- <p class="text-lg text-gray-600 max-w-2xl mx-auto">
104
- Algunos de los proyectos en los que he trabajado y que demuestran mis habilidades
105
- </p>
106
- </div>
107
-
108
- <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
109
- <!-- Proyecto 1 -->
110
- <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
111
- <div class="bg-gradient-to-br from-blue-400 to-blue-600 h-48 flex items-center justify-center">
112
- <span class="text-white text-lg font-medium">Proyecto 1</span>
113
- </div>
114
- <div class="p-6">
115
- <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
116
- <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
117
- <div class="flex space-x-3">
118
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
119
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
120
- </div>
121
- </div>
122
- </div>
123
-
124
- <!-- Proyecto 2 -->
125
- <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
126
- <div class="bg-gradient-to-br from-green-400 to-green-600 h-48 flex items-center justify-center">
127
- <span class="text-white text-lg font-medium">Proyecto 2</span>
128
- </div>
129
- <div class="p-6">
130
- <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
131
- <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
132
- <div class="flex space-x-3">
133
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
134
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
135
- </div>
136
- </div>
137
- </div>
138
-
139
- <!-- Proyecto 3 -->
140
- <div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200 project-card">
141
- <div class="bg-gradient-to-br from-purple-400 to-purple-600 h-48 flex items-center justify-center">
142
- <span class="text-white text-lg font-medium">Proyecto 3</span>
143
- </div>
144
- <div class="p-6">
145
- <h3 class="text-xl font-semibold text-gray-900 mb-3">Nombre del Proyecto</h3>
146
- <p class="text-gray-600 mb-4">Descripción breve del proyecto y las tecnologías utilizadas.</p>
147
- <div class="flex space-x-3">
148
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver código</a>
149
- <a href="#" class="text-blue-600 hover:text-blue-700 text-sm font-medium">Ver demo</a>
150
- </div>
151
- </div>
152
- </div>
153
- </div>
154
- </div>
155
- </section>
156
-
157
- <!-- Contacto -->
158
- <section id="contacto" class="py-20 bg-white">
159
- <div class="max-w-4xl mx-auto px-4">
160
- <div class="text-center mb-16">
161
- <h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Contacto</h2>
162
- <div class="w-20 h-1 bg-blue-600 mx-auto mb-8"></div>
163
- <p class="text-lg text-gray-600 max-w-2xl mx-auto">
164
- ¿Tienes un proyecto en mente? ¡Hablemos y veamos cómo puedo ayudarte!
165
- </p>
166
- </div>
167
-
168
- <div class="grid md:grid-cols-2 gap-12">
169
- <div>
170
- <h3 class="text-xl font-semibold text-gray-900 mb-6">Información de contacto</h3>
171
- <div class="space-y-4">
172
- <div class="flex items-center space-x-3">
173
- <div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
174
- <svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
175
- <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>
176
- </svg>
177
- </div>
178
- <div>
179
- <p class="text-gray-900 font-medium">Email</p>
180
- <p class="text-gray-600">tu.email@ejemplo.com</p>
181
- </div>
182
- </div>
183
-
184
- <div class="flex items-center space-x-3">
185
- <div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
186
- <svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
187
- <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>
188
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
189
- </svg>
190
- </div>
191
- <div>
192
- <p class="text-gray-900 font-medium">Ubicación</p>
193
- <p class="text-gray-600">Tu ciudad, País</p>
194
- </div>
195
- </div>
196
- </div>
197
-
198
- <div class="mt-8">
199
- <h4 class="text-lg font-medium text-gray-900 mb-4">Sígueme</h4>
200
- <div class="flex space-x-4">
201
- <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">
202
- <span class="text-gray-600 hover:text-blue-600">LI</span>
203
- </a>
204
- <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">
205
- <span class="text-gray-600 hover:text-blue-600">GH</span>
206
- </a>
207
- <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">
208
- <span class="text-gray-600 hover:text-blue-600">TW</span>
209
- </a>
210
- </div>
211
- </div>
212
- </div>
213
-
214
- <div>
215
- <form class="space-y-6">
216
- <div>
217
- <label for="name" class="block text-sm font-medium text-gray-700 mb-2">Nombre</label>
218
- <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">
219
- </div>
220
- <div>
221
- <label for="email" class="block text-sm font-medium text-gray-700 mb-2">Email</label>
222
- <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">
223
- </div>
224
- <div>
225
- <label for="message" class="block text-sm font-medium text-gray-700 mb-2">Mensaje</label>
226
- <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>
227
- </div>
228
- <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">
229
- Enviar mensaje
230
- </button>
231
- </form>
232
- </div>
233
- </div>
234
- </div>
235
- </section>
78
+ <!-- ⚠️ AQUÍ VAN LAS DEMÁS SECCIONES (SOBRE MÍ, PROYECTOS, CONTACTO) -->
79
+ <!-- Revisa los comentarios arriba para saber qué crear -->
@@ -1,7 +1,10 @@
1
1
  import { sveltekit } from '@sveltejs/kit/vite';
2
- import tailwindcss from '@tailwindcss/vite';
3
2
  import { defineConfig } from 'vite';
3
+ import tailwindcss from '@tailwindcss/vite';
4
4
 
5
5
  export default defineConfig({
6
- plugins: [sveltekit(), tailwindcss()]
6
+ plugins: [
7
+ tailwindcss(),
8
+ sveltekit()
9
+ ]
7
10
  });