create-bunspace 0.3.0 → 0.4.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.
Files changed (57) hide show
  1. package/dist/bin.js +237 -34
  2. package/dist/templates/fumadocs/MUST-FOLLOW-GUIDELINES.md +1496 -33
  3. package/dist/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +1496 -33
  4. package/dist/templates/monorepo/apps/example/package.json +1 -1
  5. package/dist/templates/monorepo/core/packages/utils/rolldown.config.ts +30 -0
  6. package/dist/templates/monorepo/tsconfig.json +3 -1
  7. package/dist/templates/telegram-bot/MUST-FOLLOW-GUIDELINES.md +1732 -0
  8. package/dist/templates/telegram-bot/bun.lock +6 -20
  9. package/dist/templates/telegram-bot/core/package.json +11 -0
  10. package/dist/templates/telegram-bot/core/rolldown.config.ts +11 -0
  11. package/dist/templates/telegram-bot/core/src/config/logging.ts +1 -3
  12. package/dist/templates/telegram-bot/core/src/handlers/config-export.ts +10 -9
  13. package/dist/templates/telegram-bot/core/src/handlers/control.ts +30 -21
  14. package/dist/templates/telegram-bot/core/src/handlers/demo-full.ts +58 -0
  15. package/dist/templates/telegram-bot/core/src/handlers/demo-keyboard.ts +49 -0
  16. package/dist/templates/telegram-bot/core/src/handlers/demo-media.ts +163 -0
  17. package/dist/templates/telegram-bot/core/src/handlers/demo-text.ts +27 -0
  18. package/dist/templates/telegram-bot/core/src/handlers/health.ts +22 -14
  19. package/dist/templates/telegram-bot/core/src/handlers/info.ts +21 -23
  20. package/dist/templates/telegram-bot/core/src/handlers/logs.ts +8 -6
  21. package/dist/templates/telegram-bot/core/src/index.ts +20 -1
  22. package/dist/templates/telegram-bot/core/src/utils/formatters.ts +5 -60
  23. package/dist/templates/telegram-bot/core/tsconfig.json +2 -0
  24. package/dist/templates/telegram-bot/package.json +3 -2
  25. package/dist/templates/telegram-bot/packages/utils/package.json +12 -1
  26. package/dist/templates/telegram-bot/packages/utils/rolldown.config.ts +11 -0
  27. package/dist/templates/telegram-bot/packages/utils/tsconfig.json +10 -0
  28. package/dist/templates/telegram-bot/tsconfig.json +7 -2
  29. package/package.json +6 -3
  30. package/templates/fumadocs/MUST-FOLLOW-GUIDELINES.md +1496 -33
  31. package/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +1496 -33
  32. package/templates/monorepo/apps/example/package.json +1 -1
  33. package/templates/monorepo/core/packages/utils/rolldown.config.ts +30 -0
  34. package/templates/monorepo/tsconfig.json +3 -1
  35. package/templates/telegram-bot/MUST-FOLLOW-GUIDELINES.md +1732 -0
  36. package/templates/telegram-bot/bun.lock +6 -20
  37. package/templates/telegram-bot/core/package.json +11 -0
  38. package/templates/telegram-bot/core/rolldown.config.ts +11 -0
  39. package/templates/telegram-bot/core/src/config/logging.ts +1 -3
  40. package/templates/telegram-bot/core/src/handlers/config-export.ts +10 -9
  41. package/templates/telegram-bot/core/src/handlers/control.ts +30 -21
  42. package/templates/telegram-bot/core/src/handlers/demo-full.ts +58 -0
  43. package/templates/telegram-bot/core/src/handlers/demo-keyboard.ts +49 -0
  44. package/templates/telegram-bot/core/src/handlers/demo-media.ts +163 -0
  45. package/templates/telegram-bot/core/src/handlers/demo-text.ts +27 -0
  46. package/templates/telegram-bot/core/src/handlers/health.ts +22 -14
  47. package/templates/telegram-bot/core/src/handlers/info.ts +21 -23
  48. package/templates/telegram-bot/core/src/handlers/logs.ts +8 -6
  49. package/templates/telegram-bot/core/src/index.ts +20 -1
  50. package/templates/telegram-bot/core/src/utils/formatters.ts +5 -60
  51. package/templates/telegram-bot/core/tsconfig.json +2 -0
  52. package/templates/telegram-bot/package.json +3 -2
  53. package/templates/telegram-bot/packages/utils/package.json +12 -1
  54. package/templates/telegram-bot/packages/utils/rolldown.config.ts +11 -0
  55. package/templates/telegram-bot/packages/utils/tsconfig.json +10 -0
  56. package/templates/telegram-bot/tsconfig.json +7 -2
  57. package/templates/telegram-bot/core/src/utils/message-builder.ts +0 -180
@@ -25,7 +25,7 @@
25
25
 
26
26
  ### Root del Monorepo
27
27
  ```
28
- mks-fumadocs-template/
28
+ mks-dev-environment/
29
29
  ├── docs/ # Documentacion del proyecto
30
30
  ├── tools/ # Scripts y herramientas de desarrollo
31
31
  ├── core/
@@ -105,29 +105,248 @@ export async function myFunction(
105
105
 
106
106
  ## REGLA 2: Logging - NUNCA console.log
107
107
 
108
- ### Obligatorio
108
+ Usar `@mks2508/better-logger` para todo el logging.
109
+
110
+ ### Imports y Setup Basico
109
111
 
110
112
  ```typescript
111
- import { createLogger } from 'mks-fumadocs-template/utils/logger';
113
+ // Singleton (recomendado para la mayoria de casos)
114
+ import logger from '@mks2508/better-logger';
112
115
 
113
- const log = createLogger('MyComponent');
116
+ // O crear instancia personalizada
117
+ import { Logger } from '@mks2508/better-logger';
118
+ const log = new Logger({
119
+ verbosity: 'debug', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
120
+ enableStackTrace: true, // Muestra archivo:linea
121
+ bufferSize: 1000, // Para exportacion de logs
122
+ });
123
+ ```
114
124
 
115
- // CORRECTO
116
- log.info('Started');
117
- log.success('Completed');
118
- log.warn('High memory usage');
119
- log.error('Failed to connect', { error });
120
- log.critical('System failure');
125
+ ### Metodos de Logging
126
+
127
+ ```typescript
128
+ // Niveles basicos
129
+ logger.debug('Debug message', { context });
130
+ logger.info('Info message');
131
+ logger.warn('Warning message');
132
+ logger.error('Error message', errorObject);
133
+ logger.success('Operation completed');
134
+ logger.critical('System failure!');
135
+ logger.trace('Trace from nested function');
136
+ ```
137
+
138
+ ### Scoped Loggers (USAR SIEMPRE en servicios)
139
+
140
+ ```typescript
141
+ // ComponentLogger - Para componentes UI y servicios
142
+ const authLog = logger.component('AuthService');
143
+ authLog.info('Usuario autenticando...'); // [COMPONENT] [AuthService] Usuario...
144
+ authLog.success('Login exitoso');
145
+ authLog.lifecycle('mount', 'Component mounted');
146
+ authLog.stateChange('idle', 'loading');
147
+
148
+ // APILogger - Para endpoints y llamadas HTTP
149
+ const apiLog = logger.api('UserAPI');
150
+ apiLog.info('GET /users'); // [API] [UserAPI] GET /users
151
+ apiLog.slow('Response slow', 2500); // [API] [SLOW] Response slow (2500ms)
152
+ apiLog.rateLimit('Too many requests'); // [API] [RATE_LIMIT]
153
+ apiLog.deprecated('Use /v2/users instead');
154
+
155
+ // ScopedLogger - Generico con contextos anidados
156
+ const dbLog = logger.scope('Database');
157
+ dbLog.info('Query executing');
158
+ dbLog.context('transactions').run(() => {
159
+ dbLog.info('Inside transaction'); // [Database:transactions] Inside...
160
+ });
161
+ ```
162
+
163
+ ### Timing y Performance
164
+
165
+ ```typescript
166
+ // Medir operaciones
167
+ logger.time('db-query');
168
+ await db.query('SELECT * FROM users');
169
+ logger.timeEnd('db-query'); // Timer: db-query - 234.56ms
170
+
171
+ // En scoped loggers
172
+ const serviceLog = logger.component('ProductService');
173
+ serviceLog.time('fetch-products');
174
+ const products = await fetchProducts();
175
+ serviceLog.timeEnd('fetch-products');
176
+ ```
177
+
178
+ ### Badges para Contexto
179
+
180
+ ```typescript
181
+ // Badges encadenables
182
+ logger.badges(['CACHE', 'HIT']).info('Data from cache');
183
+ logger.badge('v2').badge('stable').info('API response');
184
+
185
+ // En scoped loggers
186
+ const api = logger.api('GraphQL');
187
+ api.badges(['mutation', 'user']).info('createUser executed');
188
+ ```
189
+
190
+ ### Transports (Envio de Logs)
191
+
192
+ ```typescript
193
+ import logger, {
194
+ FileTransport,
195
+ HttpTransport,
196
+ addTransport
197
+ } from '@mks2508/better-logger';
198
+
199
+ // Transport a archivo (solo Node.js/Bun)
200
+ logger.addTransport({
201
+ target: 'file',
202
+ options: {
203
+ destination: '/var/log/app.log',
204
+ batchSize: 100,
205
+ flushInterval: 5000 // ms
206
+ },
207
+ level: 'warn' // Solo warn+ van al archivo
208
+ });
209
+
210
+ // Transport HTTP (envia a servidor de logs)
211
+ logger.addTransport({
212
+ target: 'http',
213
+ options: {
214
+ url: 'https://logs.example.com/ingest',
215
+ headers: { 'Authorization': 'Bearer xxx' },
216
+ batchSize: 50,
217
+ flushInterval: 10000
218
+ }
219
+ });
220
+
221
+ // Flush manual antes de cerrar
222
+ await logger.flushTransports();
223
+ await logger.closeTransports();
224
+ ```
225
+
226
+ ### Hooks y Middleware
227
+
228
+ ```typescript
229
+ // Hook beforeLog - agregar metadata
230
+ logger.on('beforeLog', (entry) => {
231
+ entry.correlationId = getCorrelationId();
232
+ entry.userId = getCurrentUserId();
233
+ return entry;
234
+ });
235
+
236
+ // Hook afterLog - side effects
237
+ logger.on('afterLog', (entry) => {
238
+ if (entry.level === 'error') {
239
+ sendToErrorTracking(entry);
240
+ }
241
+ });
242
+
243
+ // Middleware - pipeline de procesamiento
244
+ logger.use((entry, next) => {
245
+ // Enriquecer con request context
246
+ const store = asyncLocalStorage.getStore();
247
+ if (store?.requestId) {
248
+ entry.requestId = store.requestId;
249
+ }
250
+ next();
251
+ });
252
+ ```
253
+
254
+ ### Serializers Personalizados
255
+
256
+ ```typescript
257
+ // Serializar errores de forma estructurada
258
+ logger.addSerializer(Error, (err) => ({
259
+ name: err.name,
260
+ message: err.message,
261
+ stack: err.stack?.split('\n').slice(0, 5),
262
+ code: (err as any).code
263
+ }));
264
+
265
+ // Serializar objetos custom
266
+ logger.addSerializer(User, (user) => ({
267
+ id: user.id,
268
+ email: '[REDACTED]',
269
+ role: user.role
270
+ }));
271
+ ```
272
+
273
+ ### Configuracion Frontend vs Backend
274
+
275
+ ```typescript
276
+ // === BACKEND (Node.js/Bun) ===
277
+ import logger from '@mks2508/better-logger';
278
+
279
+ // Preset optimizado para terminal
280
+ logger.preset('cyberpunk');
281
+ logger.showTimestamp();
282
+ logger.showLocation();
283
+
284
+ // Transport a archivo
285
+ logger.addTransport({
286
+ target: 'file',
287
+ options: { destination: './logs/app.log' }
288
+ });
289
+
290
+ // === FRONTEND (Browser) ===
291
+ import logger from '@mks2508/better-logger';
292
+
293
+ // Preset con colores CSS
294
+ logger.preset('default');
295
+ logger.hideLocation(); // No util en browser
296
+
297
+ // Transport HTTP para enviar errores
298
+ logger.addTransport({
299
+ target: 'http',
300
+ options: { url: '/api/logs' },
301
+ level: 'error' // Solo errores al servidor
302
+ });
303
+ ```
304
+
305
+ ### Verbosity y Filtrado
306
+
307
+ ```typescript
308
+ // Cambiar nivel de verbosidad
309
+ logger.setVerbosity('warn'); // Solo warn, error, critical
310
+ logger.setVerbosity('silent'); // Desactiva todo
311
+ logger.setVerbosity('debug'); // Muestra todo
312
+
313
+ // Configuracion condicional
314
+ if (process.env.NODE_ENV === 'production') {
315
+ logger.setVerbosity('warn');
316
+ logger.hideLocation();
317
+ }
318
+ ```
319
+
320
+ ### Utilidades de Grupos y Tablas
321
+
322
+ ```typescript
323
+ // Tablas de datos
324
+ logger.table([
325
+ { name: 'Alice', age: 30 },
326
+ { name: 'Bob', age: 25 }
327
+ ]);
328
+
329
+ // Grupos colapsables
330
+ logger.group('Database Operations');
331
+ logger.info('Connecting...');
332
+ logger.info('Querying...');
333
+ logger.groupEnd();
334
+
335
+ // Grupo colapsado por defecto
336
+ logger.group('Debug Details', true);
337
+ logger.debug('Verbose info here');
338
+ logger.groupEnd();
121
339
  ```
122
340
 
123
341
  ### Prohibido
124
342
 
125
343
  ```typescript
126
- // INCORRECTO
344
+ // INCORRECTO - NUNCA usar
127
345
  console.log('Started');
128
346
  console.error('Failed');
129
347
  console.info('Info');
130
348
  console.warn('Warning');
349
+ console.debug('Debug');
131
350
  ```
132
351
 
133
352
  ---
@@ -136,43 +355,212 @@ console.warn('Warning');
136
355
 
137
356
  ### Obligatorio
138
357
 
139
- TODA operacion que pueda fallar DEBE usar `Result<T, E>` del package `mks-fumadocs-template/utils/result`:
358
+ TODA operacion que pueda fallar DEBE usar `Result<T, E>` del package `@mks2508/no-throw`:
140
359
 
141
360
  ```typescript
142
361
  import {
143
- ok,
144
- tryCatch,
145
- type Result
146
- } from 'mks-fumadocs-template/utils/result';
147
- import {
148
- createAppError,
149
- AppErrorCode
150
- } from 'mks-fumadocs-template/utils/result';
362
+ ok, err, fail,
363
+ isOk, isErr,
364
+ map, mapErr, flatMap,
365
+ match,
366
+ tryCatch, tryCatchAsync, fromPromise,
367
+ unwrap, unwrapOr, unwrapOrElse,
368
+ tap, tapErr,
369
+ collect, all,
370
+ type Result, type ResultError
371
+ } from '@mks2508/no-throw';
372
+ ```
151
373
 
152
- async function fetchData(
153
- url: string
154
- ): Promise<Result<string, AppError>> {
155
- const result = await tryCatch(
374
+ ### Constructores
375
+
376
+ ```typescript
377
+ // Crear resultado exitoso
378
+ const success = ok(42); // Result<number, never>
379
+ const success2 = ok({ name: 'John' }); // Result<{name: string}, never>
380
+
381
+ // Crear resultado de error
382
+ const error = err('Something failed'); // Result<never, string>
383
+
384
+ // Crear error estructurado con fail()
385
+ const structuredError = fail(
386
+ 'NETWORK_ERROR', // code
387
+ 'Failed to fetch data', // message
388
+ originalError // cause (opcional)
389
+ );
390
+ // Retorna: Result<never, ResultError<'NETWORK_ERROR'>>
391
+ ```
392
+
393
+ ### Type Guards
394
+
395
+ ```typescript
396
+ const result = await fetchData(url);
397
+
398
+ if (isOk(result)) {
399
+ console.log(result.value); // Tipo: T
400
+ }
401
+
402
+ if (isErr(result)) {
403
+ console.log(result.error); // Tipo: E
404
+ }
405
+ ```
406
+
407
+ ### Transformaciones
408
+
409
+ ```typescript
410
+ // map - transforma el valor si es Ok
411
+ const doubled = map(result, (n) => n * 2);
412
+
413
+ // mapErr - transforma el error si es Err
414
+ const mappedErr = mapErr(result, (e) => ({ ...e, timestamp: Date.now() }));
415
+
416
+ // flatMap - encadena operaciones que retornan Result
417
+ const chained = flatMap(result, (value) => {
418
+ if (value > 100) return err('Too large');
419
+ return ok(value * 2);
420
+ });
421
+ ```
422
+
423
+ ### Pattern Matching
424
+
425
+ ```typescript
426
+ const message = match(result, {
427
+ ok: (value) => `Success: ${value}`,
428
+ err: (error) => `Error: ${error.message}`
429
+ });
430
+ ```
431
+
432
+ ### Manejo de Excepciones
433
+
434
+ ```typescript
435
+ // tryCatch - para operaciones sincronas
436
+ const syncResult = tryCatch(
437
+ () => JSON.parse(jsonString),
438
+ 'PARSE_ERROR'
439
+ );
440
+
441
+ // tryCatchAsync - para operaciones async
442
+ const asyncResult = await tryCatchAsync(
443
+ async () => {
444
+ const response = await fetch(url);
445
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
446
+ return response.json();
447
+ },
448
+ 'NETWORK_ERROR'
449
+ );
450
+
451
+ // fromPromise - convierte Promise a Result
452
+ const promiseResult = await fromPromise(
453
+ fetch(url).then(r => r.json()),
454
+ 'FETCH_ERROR'
455
+ );
456
+ ```
457
+
458
+ ### Unwrap
459
+
460
+ ```typescript
461
+ // unwrap - obtiene valor o lanza error
462
+ const value = unwrap(result); // Throws si es Err
463
+
464
+ // unwrapOr - valor por defecto
465
+ const valueOrDefault = unwrapOr(result, 0);
466
+
467
+ // unwrapOrElse - valor calculado
468
+ const valueOrComputed = unwrapOrElse(result, (error) => {
469
+ log.error('Using fallback due to:', error);
470
+ return defaultValue;
471
+ });
472
+ ```
473
+
474
+ ### Efectos Secundarios
475
+
476
+ ```typescript
477
+ // tap - ejecuta efecto si es Ok (no modifica resultado)
478
+ const logged = tap(result, (value) => {
479
+ log.info('Got value:', value);
480
+ });
481
+
482
+ // tapErr - ejecuta efecto si es Err
483
+ const errorLogged = tapErr(result, (error) => {
484
+ log.error('Operation failed:', error);
485
+ });
486
+ ```
487
+
488
+ ### Colecciones
489
+
490
+ ```typescript
491
+ // collect - convierte array de Results en Result de array
492
+ const results: Result<number, string>[] = [ok(1), ok(2), ok(3)];
493
+ const collected = collect(results); // Result<number[], string>
494
+
495
+ // all - igual que collect (alias)
496
+ const allResults = all([ok(1), ok(2), ok(3)]);
497
+ ```
498
+
499
+ ### Ejemplo Completo
500
+
501
+ ```typescript
502
+ import { ok, fail, isErr, tryCatchAsync, match } from '@mks2508/no-throw';
503
+ import logger from '@mks2508/better-logger';
504
+
505
+ const log = logger.component('UserService');
506
+
507
+ async function fetchUser(
508
+ id: string
509
+ ): Promise<Result<IUser, ResultError<'NOT_FOUND' | 'NETWORK_ERROR'>>> {
510
+ const result = await tryCatchAsync(
156
511
  async () => {
157
- const response = await fetch(url);
512
+ const response = await fetch(`/api/users/${id}`);
513
+ if (response.status === 404) {
514
+ throw { code: 'NOT_FOUND' };
515
+ }
158
516
  if (!response.ok) {
159
517
  throw new Error(`HTTP ${response.status}`);
160
518
  }
161
- return await response.text();
519
+ return await response.json();
162
520
  },
163
- AppErrorCode.NetworkError
521
+ 'NETWORK_ERROR'
164
522
  );
165
523
 
166
- if (result.isErr()) {
167
- return createAppError(
168
- AppErrorCode.NetworkError,
169
- `Failed to fetch from ${url}`,
170
- result.error
171
- );
524
+ if (isErr(result)) {
525
+ const error = result.error;
526
+ if (error.cause?.code === 'NOT_FOUND') {
527
+ return fail('NOT_FOUND', `User ${id} not found`);
528
+ }
529
+ return fail('NETWORK_ERROR', `Failed to fetch user ${id}`, error);
172
530
  }
173
531
 
174
532
  return ok(result.value);
175
533
  }
534
+
535
+ // Uso
536
+ const userResult = await fetchUser('123');
537
+ const message = match(userResult, {
538
+ ok: (user) => {
539
+ log.success(`User loaded: ${user.name}`);
540
+ return user;
541
+ },
542
+ err: (error) => {
543
+ log.error(`Failed: ${error.message}`);
544
+ return null;
545
+ }
546
+ });
547
+ ```
548
+
549
+ ### Prohibido
550
+
551
+ ```typescript
552
+ // INCORRECTO - NUNCA usar try/catch directo sin Result
553
+ try {
554
+ const data = await fetchData();
555
+ } catch (e) {
556
+ console.error(e);
557
+ }
558
+
559
+ // INCORRECTO - NUNCA lanzar excepciones
560
+ throw new Error('Something failed');
561
+
562
+ // CORRECTO - Siempre retornar Result
563
+ return fail('ERROR_CODE', 'Description', cause);
176
564
  ```
177
565
 
178
566
  ---
@@ -259,11 +647,1086 @@ Antes de hacer commit de codigo, verificar:
259
647
 
260
648
  ---
261
649
 
650
+ ## REGLA 7: TransactionState - Estados de UI
651
+
652
+ ### Estados Soportados
653
+
654
+ | Estado | Uso |
655
+ |--------|-----|
656
+ | `initial` | Estado inicial, sin datos |
657
+ | `loading` | Primera carga en progreso |
658
+ | `revalidating` | Recargando con datos previos |
659
+ | `success` | Operacion completada con exito |
660
+ | `failed` | Error en la operacion |
661
+
662
+ ### Factories y Guards
663
+
664
+ ```typescript
665
+ // Factories
666
+ createInitialState()
667
+ createLoadingState(message?: string)
668
+ createRevalidatingState(previousData: T)
669
+ createSuccessState(data: T, message?: string)
670
+ createFailedState(error: E)
671
+
672
+ // Guards
673
+ isInitial(state) // true si initial
674
+ isLoading(state) // true si loading
675
+ isRevalidating(state)
676
+ isSuccess(state)
677
+ isFailed(state)
678
+ isPending(state) // loading OR revalidating
679
+ isCompleted(state) // success OR failed
680
+ hasData(state) // success OR revalidating
681
+ ```
682
+
683
+ ### Patron en Hooks
684
+
685
+ ```typescript
686
+ const [state, setState] = useState<TransactionState<T, E>>(createInitialState);
687
+
688
+ const load = useCallback(async () => {
689
+ setState(createLoadingState('Cargando...'));
690
+ const result = await service.fetch();
691
+ if (isSuccess(result)) {
692
+ setState(createSuccessState(result.data));
693
+ } else {
694
+ setState(createFailedState(result.error));
695
+ }
696
+ }, []);
697
+ ```
698
+
699
+ ---
700
+
701
+ ## REGLA 8: Arquitectura BLO (Business Logic Layer)
702
+
703
+ ### Principios Fundamentales
704
+
705
+ | Principio | Descripcion |
706
+ |-----------|-------------|
707
+ | **Separacion clara** | UI y logica de negocio en capas distintas |
708
+ | **Handlers** | Clases puras TypeScript sin dependencias React |
709
+ | **Hooks** | Puente reactivo entre handlers y componentes |
710
+ | **Componentes** | Solo UI y eventos, sin logica de negocio |
711
+
712
+ ### Clasificacion de Componentes
713
+
714
+ #### Componentes UI Reutilizables (`components/ui/`)
715
+ - **Alta reusabilidad** en diferentes contextos
716
+ - **Baja complejidad** y focalizacion especifica
717
+ - **Estructura simplificada** (archivo unico o pocos archivos)
718
+ - Ejemplos: Button, Input, Card, Modal
719
+
720
+ #### Componentes de Dominio (`components/`)
721
+ - **Alta complejidad** y logica de negocio especifica
722
+ - **Estructura completa** con handler y hook
723
+ - **Subcomponentes** si es necesario
724
+ - Ejemplos: ProductCard, UserProfile, OrderDetails
725
+
726
+ ### Estructura Completa (Componentes Complejos)
727
+
728
+ ```
729
+ components/
730
+ └── ProductCard/
731
+ ├── index.tsx # Componente React puro
732
+ ├── ProductCard.types.ts # Interfaces y tipos
733
+ ├── ProductCard.styles.ts # Clases Tailwind con CVA
734
+ ├── ProductCard.handler.ts # Logica de negocio (BLO)
735
+ ├── ProductCard.hook.ts # Hook React con estado
736
+ └── components/ # Subcomponentes (opcional)
737
+ ├── ProductImage/
738
+ └── ProductActions/
739
+ ```
740
+
741
+ ### Componente React (`index.tsx`)
742
+
743
+ ```typescript
744
+ import React from 'react';
745
+ import { IProductCardProps } from './ProductCard.types';
746
+ import { styles } from './ProductCard.styles';
747
+ import { useProductCard } from './ProductCard.hook';
748
+
749
+ export const ProductCard: React.FC<IProductCardProps> = (props) => {
750
+ const { state, actions } = useProductCard(props);
751
+
752
+ return (
753
+ <div className={styles.container}>
754
+ {/* Solo UI y eventos - sin logica de negocio */}
755
+ <h3 className={styles.title}>{state.data?.name}</h3>
756
+ <button onClick={actions.addToCart}>Agregar</button>
757
+ </div>
758
+ );
759
+ };
760
+ ```
761
+
762
+ **Reglas del componente:**
763
+ - ✅ Solo UI: renderizado y eventos
764
+ - ✅ Props inmutables: no modificar props directamente
765
+ - ✅ Estado delegado: usar hook para estado y acciones
766
+ - ❌ Sin logica: no business logic en el componente
767
+
768
+ ### Tipos TypeScript (`.types.ts`)
769
+
770
+ ```typescript
771
+ export interface IProductCardProps {
772
+ productId: string;
773
+ initialData?: IProduct;
774
+ autoLoad?: boolean;
775
+ className?: string;
776
+ onAddToCart?: (id: string) => void;
777
+ }
778
+
779
+ export interface IProductCardState {
780
+ isLoading: boolean;
781
+ data: IProduct | null;
782
+ error: string | null;
783
+ }
784
+
785
+ export interface IProductCardActions {
786
+ loadProduct: () => Promise<void>;
787
+ addToCart: () => void;
788
+ reset: () => void;
789
+ }
790
+ ```
791
+
792
+ ### Estilos Tailwind con CVA (`.styles.ts`)
793
+
794
+ ```typescript
795
+ import { cva } from 'class-variance-authority';
796
+
797
+ export const containerVariants = cva(
798
+ "w-full p-4 bg-white border rounded-lg transition-shadow",
799
+ {
800
+ variants: {
801
+ variant: {
802
+ default: "border-gray-200 hover:shadow-md",
803
+ featured: "border-blue-200 bg-blue-50 hover:shadow-lg",
804
+ compact: "p-2 border-gray-100",
805
+ },
806
+ size: {
807
+ sm: "max-w-xs",
808
+ md: "max-w-sm",
809
+ lg: "max-w-md",
810
+ }
811
+ },
812
+ defaultVariants: {
813
+ variant: "default",
814
+ size: "md",
815
+ }
816
+ }
817
+ );
818
+
819
+ export const styles = {
820
+ container: containerVariants,
821
+ header: "flex items-center justify-between mb-4",
822
+ title: "text-lg font-semibold text-gray-900",
823
+ content: "text-gray-600 min-h-[100px]",
824
+ actions: "flex gap-2 mt-4 pt-4 border-t border-gray-200",
825
+ loading: "flex items-center justify-center py-8 text-gray-500",
826
+ error: "flex items-center justify-center py-8 text-red-500 bg-red-50 rounded",
827
+ };
828
+ ```
829
+
830
+ ### Handler BLO (`.handler.ts`)
831
+
832
+ ```typescript
833
+ import { IProductCardState } from './ProductCard.types';
834
+
835
+ export class ProductCardHandler {
836
+ private state: IProductCardState = {
837
+ isLoading: false,
838
+ data: null,
839
+ error: null,
840
+ };
841
+
842
+ constructor(initialData?: IProduct) {
843
+ if (initialData) {
844
+ this.state.data = initialData;
845
+ }
846
+ }
847
+
848
+ getState(): IProductCardState {
849
+ return { ...this.state }; // Siempre retornar copia
850
+ }
851
+
852
+ async loadProduct(productId: string): Promise<void> {
853
+ this.state.isLoading = true;
854
+ this.state.error = null;
855
+
856
+ try {
857
+ // Logica de negocio pura - sin React
858
+ const response = await fetch(`/api/products/${productId}`);
859
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
860
+ this.state.data = await response.json();
861
+ } catch (error) {
862
+ this.state.error = error instanceof Error ? error.message : 'Error';
863
+ } finally {
864
+ this.state.isLoading = false;
865
+ }
866
+ }
867
+
868
+ reset(): void {
869
+ this.state = { isLoading: false, data: null, error: null };
870
+ }
871
+ }
872
+ ```
873
+
874
+ **Reglas del handler:**
875
+ - ✅ Clase pura TypeScript: sin dependencias de React
876
+ - ✅ Estado inmutable: siempre retornar copias
877
+ - ✅ Logica de negocio: validaciones, transformaciones, API calls
878
+ - ✅ Testing facil: sin dependencias externas
879
+ - ❌ Sin React: no hooks, no JSX, no estado React
880
+
881
+ ### Hook React (`.hook.ts`)
882
+
883
+ ```typescript
884
+ import { useState, useCallback, useEffect } from 'react';
885
+ import { IProductCardProps, IProductCardState, IProductCardActions } from './ProductCard.types';
886
+ import { ProductCardHandler } from './ProductCard.handler';
887
+
888
+ export const useProductCard = (props: IProductCardProps) => {
889
+ const [handler] = useState(() => new ProductCardHandler(props.initialData));
890
+ const [state, setState] = useState<IProductCardState>(handler.getState());
891
+
892
+ const loadProduct = useCallback(async () => {
893
+ await handler.loadProduct(props.productId);
894
+ setState(handler.getState());
895
+ }, [handler, props.productId]);
896
+
897
+ const addToCart = useCallback(() => {
898
+ if (state.data && props.onAddToCart) {
899
+ props.onAddToCart(state.data.id);
900
+ }
901
+ }, [state.data, props.onAddToCart]);
902
+
903
+ const reset = useCallback(() => {
904
+ handler.reset();
905
+ setState(handler.getState());
906
+ }, [handler]);
907
+
908
+ useEffect(() => {
909
+ if (props.autoLoad) {
910
+ loadProduct();
911
+ }
912
+ }, [props.autoLoad, loadProduct]);
913
+
914
+ return {
915
+ state,
916
+ actions: { loadProduct, addToCart, reset } as IProductCardActions,
917
+ };
918
+ };
919
+ ```
920
+
921
+ **Reglas del hook:**
922
+ - ✅ Puente reactivo: entre handler y componente
923
+ - ✅ Estado React: sincronizado con handler
924
+ - ✅ Callbacks memorizados: useCallback para optimizacion
925
+ - ✅ Efectos controlados: auto-load, dependencies
926
+ - ✅ Interface consistente: siempre retorna `{ state, actions }`
927
+
928
+ ### Nombres de Archivos
929
+
930
+ ```typescript
931
+ // CORRECTO - PascalCase
932
+ ProductCard.tsx
933
+ UserProfile.tsx
934
+ DataTable.tsx
935
+
936
+ // INCORRECTO
937
+ my-component.tsx
938
+ user_profile.tsx
939
+ dataTable.tsx
940
+ ```
941
+
942
+ ### Generacion con mks-ui CLI
943
+
944
+ ```bash
945
+ # Componente UI simple (archivo unico)
946
+ mks-ui component Button --ui
947
+
948
+ # Componente complejo con BLO
949
+ mks-ui component ProductCard --complex
950
+
951
+ # Vista previa sin crear
952
+ mks-ui component TestComponent --dry-run
953
+
954
+ # Servicio backend
955
+ mks-ui service ProductService
956
+ ```
957
+
958
+ ---
959
+
960
+ ## REGLA 9: Zustand con Slices
961
+
962
+ ### Store Central por Dominio
963
+
964
+ ```typescript
965
+ // stores/app.store.ts
966
+ import { create } from 'zustand';
967
+
968
+ interface IAppStore {
969
+ // UI Slice
970
+ ui: { sidebarOpen: boolean; theme: 'light' | 'dark' };
971
+ toggleSidebar: () => void;
972
+
973
+ // Data Slice
974
+ users: IUser[];
975
+ setUsers: (users: IUser[]) => void;
976
+ }
977
+
978
+ export const useAppStore = create<IAppStore>((set) => ({
979
+ ui: { sidebarOpen: true, theme: 'light' },
980
+ toggleSidebar: () => set((s) => ({
981
+ ui: { ...s.ui, sidebarOpen: !s.ui.sidebarOpen }
982
+ })),
983
+
984
+ users: [],
985
+ setUsers: (users) => set({ users }),
986
+ }));
987
+ ```
988
+
989
+ ### Selectores Puros
990
+
991
+ ```typescript
992
+ // CORRECTO - Selector especifico previene renders
993
+ const theme = useAppStore((s) => s.ui.theme);
994
+
995
+ // INCORRECTO - Re-render en cualquier cambio
996
+ const store = useAppStore();
997
+ const theme = store.ui.theme;
998
+ ```
999
+
1000
+ ---
1001
+
1002
+ ## REGLA 10: Handler Pattern
1003
+
1004
+ ### Fachada de Orquestacion
1005
+
1006
+ ```typescript
1007
+ // handlers/useUserHandler.ts
1008
+ export const useUserHandler = (service: UserService) => {
1009
+ const { state, load } = useUserLoader(service);
1010
+ const setUsers = useAppStore((s) => s.setUsers);
1011
+
1012
+ const refresh = useCallback(async (id: string) => {
1013
+ const result = await load(id);
1014
+ if (isSuccess(result)) {
1015
+ setUsers([result.data]);
1016
+ }
1017
+ return result;
1018
+ }, [load, setUsers]);
1019
+
1020
+ return useMemo(() => ({
1021
+ isLoading: isPending(state),
1022
+ hasError: isFailed(state),
1023
+ user: hasData(state) ? state.data : null,
1024
+ refresh,
1025
+ }), [state, refresh]);
1026
+ };
1027
+ ```
1028
+
1029
+ ### Reglas del Handler
1030
+
1031
+ - API publica memorizada con `useMemo`
1032
+ - Callbacks con `useCallback`
1033
+ - No exponer detalles internos (state crudo)
1034
+ - No usar `useEffect` salvo sincronizacion externa
1035
+
1036
+ ---
1037
+
1038
+ ## REGLA 11: BaseService para HTTP
1039
+
1040
+ ### Clase Base
1041
+
1042
+ ```typescript
1043
+ export abstract class BaseService {
1044
+ constructor(
1045
+ protected readonly baseUrl: string,
1046
+ protected readonly fetchImpl = fetch
1047
+ ) {}
1048
+
1049
+ protected async request<T>({
1050
+ path,
1051
+ method = 'GET',
1052
+ body,
1053
+ timeoutMs = 15000,
1054
+ signal,
1055
+ }: IRequestOptions): Promise<Response> {
1056
+ const controller = new AbortController();
1057
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
1058
+
1059
+ try {
1060
+ return await this.fetchImpl(`${this.baseUrl}${path}`, {
1061
+ method,
1062
+ headers: { 'content-type': 'application/json' },
1063
+ body: body ? JSON.stringify(body) : undefined,
1064
+ signal: signal ?? controller.signal,
1065
+ });
1066
+ } finally {
1067
+ clearTimeout(timeoutId);
1068
+ }
1069
+ }
1070
+ }
1071
+ ```
1072
+
1073
+ ### Error Taxonomy
1074
+
1075
+ ```typescript
1076
+ export type ServiceError =
1077
+ | { kind: 'timeout'; message: string }
1078
+ | { kind: 'unauthorized'; message: string } // 401
1079
+ | { kind: 'forbidden'; message: string } // 403
1080
+ | { kind: 'not_found'; message: string } // 404
1081
+ | { kind: 'conflict'; message: string } // 409
1082
+ | { kind: 'too_many_requests'; message: string } // 429
1083
+ | { kind: 'server_error'; message: string; status: number } // 5xx
1084
+ | { kind: 'decode_error'; message: string }
1085
+ | { kind: 'unexpected'; message: string };
1086
+ ```
1087
+
1088
+ ### Servicio Tipado
1089
+
1090
+ ```typescript
1091
+ export class UserService extends BaseService {
1092
+ async getUser(id: string): Promise<Result<IUser, ServiceError>> {
1093
+ try {
1094
+ const res = await this.request({ path: `/users/${id}` });
1095
+
1096
+ if (res.status === 401) return err({ kind: 'unauthorized', message: 'No autenticado' });
1097
+ if (res.status === 403) return err({ kind: 'forbidden', message: 'No autorizado' });
1098
+ if (res.status === 404) return err({ kind: 'not_found', message: 'Usuario no encontrado' });
1099
+ if (!res.ok) return err({ kind: 'server_error', message: 'Error servidor', status: res.status });
1100
+
1101
+ const json = await res.json();
1102
+ const parsed = UserSchema.safeParse(json);
1103
+ if (!parsed.success) return err({ kind: 'decode_error', message: parsed.error.message });
1104
+
1105
+ return ok(parsed.data);
1106
+ } catch (e) {
1107
+ if ((e as Error)?.name === 'AbortError') {
1108
+ return err({ kind: 'timeout', message: 'Timeout' });
1109
+ }
1110
+ return err({ kind: 'unexpected', message: (e as Error)?.message ?? 'Error inesperado' });
1111
+ }
1112
+ }
1113
+ }
1114
+ ```
1115
+
1116
+ ---
1117
+
1118
+ ## REGLA 12: React Best Practices
1119
+
1120
+ ### Minimizar useEffect
1121
+
1122
+ ```typescript
1123
+ // CORRECTO - useMemo para derivar datos
1124
+ const filteredUsers = useMemo(
1125
+ () => users.filter(u => u.active),
1126
+ [users]
1127
+ );
1128
+
1129
+ // INCORRECTO - useEffect para derivar datos
1130
+ const [filteredUsers, setFiltered] = useState([]);
1131
+ useEffect(() => {
1132
+ setFiltered(users.filter(u => u.active));
1133
+ }, [users]);
1134
+ ```
1135
+
1136
+ ### memo para Componentes Puros
1137
+
1138
+ ```typescript
1139
+ // CORRECTO
1140
+ export const UserCard = memo(({ user }: IUserCardProps) => (
1141
+ <div>{user.name}</div>
1142
+ ));
1143
+
1144
+ // useState solo para estado efimero local
1145
+ const [inputValue, setInputValue] = useState('');
1146
+ ```
1147
+
1148
+ ---
1149
+
1150
+ ## Checklist Pre-Commit
1151
+
1152
+ Antes de hacer commit de codigo, verificar:
1153
+
1154
+ - [ ] Todo codigo nuevo tiene JSDoc completo
1155
+ - [ ] No hay `console.log/debug/error/info/warn`
1156
+ - [ ] Todo lo que puede fallar usa `Result<T, E>`
1157
+ - [ ] Interfaces tienen prefijo `I`
1158
+ - [ ] Barrel exports en todas las carpetas
1159
+ - [ ] Async/await en lugar de Promise chaining
1160
+ - [ ] Componentes UI siguen estructura de archivos
1161
+ - [ ] Estados de UI usan TransactionState
1162
+ - [ ] Stores usan selectores puros
1163
+ - [ ] Handlers exponen API memorizada
1164
+ - [ ] `bun run typecheck` pasa
1165
+ - [ ] `bun run lint` pasa
1166
+ - [ ] `bun run format` aplicado
1167
+
1168
+ ---
1169
+
1170
+ ## REGLA 13: UI Components - Shadcn/UI + Animate UI
1171
+
1172
+ ### Stack de Componentes UI
1173
+
1174
+ **OBLIGATORIO**: Shadcn/UI como base, con **Animate UI como fuente principal** de componentes animados.
1175
+
1176
+ ### Formato de Comandos
1177
+
1178
+ ```bash
1179
+ # CORRECTO - Formato estandar con Bun
1180
+ bunx --bun shadcn@latest add @animate-ui/primitives-texts-sliding-number
1181
+ bunx --bun shadcn@latest add @animate-ui/primitives-button
1182
+ bunx --bun shadcn@latest add @animate-ui/primitives-card
1183
+
1184
+ # CORRECTO - Componentes base Shadcn (cuando no exista en Animate UI)
1185
+ bunx --bun shadcn@latest add button
1186
+ bunx --bun shadcn@latest add card
1187
+ bunx --bun shadcn@latest add dialog
1188
+
1189
+ # Ver registry disponible
1190
+ # https://ui.shadcn.com/docs/mcp
1191
+ ```
1192
+
1193
+ ### Fuentes de Componentes
1194
+
1195
+ | Fuente | Prefijo | Tipo | Prioridad |
1196
+ |--------|---------|------|-----------|
1197
+ | **Animate UI** | `@animate-ui/primitives-*` | Componentes con animaciones | **PREFERIDO** |
1198
+ | **Shadcn Registry** | `shadcn/*` | Componentes base estándar | Alternativa |
1199
+ | **Radix UI** | `@radix-ui/*` | Primitivas sin estilos | Solo si es necesario |
1200
+
1201
+ ### Configuracion de components.json
1202
+
1203
+ ```json
1204
+ {
1205
+ "$schema": "https://ui.shadcn.com/schema.json",
1206
+ "style": "new-york",
1207
+ "rsc": false,
1208
+ "tsx": true,
1209
+ "tailwind": {
1210
+ "config": "tailwind.config.ts",
1211
+ "css": "src/app/globals.css",
1212
+ "baseColor": "neutral",
1213
+ "cssVariables": true,
1214
+ "prefix": ""
1215
+ },
1216
+ "aliases": {
1217
+ "components": "@/components",
1218
+ "utils": "@/lib/utils",
1219
+ "ui": "@/components/ui"
1220
+ },
1221
+ "registry": "https://ui.shadcn.com"
1222
+ }
1223
+ ```
1224
+
1225
+ ### Patrones de Importacion
1226
+
1227
+ ```typescript
1228
+ // CORRECTO - Importar desde components/ui
1229
+ import { Button } from '@/components/ui/button';
1230
+ import { Card } from '@/components/ui/card';
1231
+
1232
+ // CORRECTO - Componentes Animate UI
1233
+ import { SlidingNumber } from '@/components/ui/sliding-number';
1234
+ import { AnimatedCard } from '@/components/ui/animated-card';
1235
+ import { MorphingText } from '@/components/ui/morphing-text';
1236
+
1237
+ // INCORRECTO - Importar directo desde node_modules
1238
+ import { Button } from '@radix-ui/react-button';
1239
+ ```
1240
+
1241
+ ### Componentes Recomendados (Animate UI)
1242
+
1243
+ | Componente | Prefijo | Uso |
1244
+ |-----------|---------|-----|
1245
+ | `sliding-number` | `@animate-ui/primitives-texts-*` | Números animados, contadores |
1246
+ | `morphing-text` | `@animate-ui/primitives-texts-*` | Texto que transforma entre valores |
1247
+ | `animated-card` | `@animate-ui/primitives-card*` | Tarjetas con animaciones de entrada |
1248
+ | `progress-reveal` | `@animate-ui/primitives-progress-*` | Progress con animación suave |
1249
+ | `slide-toggle` | `@animate-ui/primitives-toggle-*` | Switch con animación |
1250
+
1251
+ ### Componentes Base Shadcn (cuando Animate UI no disponible)
1252
+
1253
+ ```bash
1254
+ # Instalar componentes base esenciales
1255
+ bunx --bun shadcn@latest add button
1256
+ bunx --bun shadcn@latest add card
1257
+ bunx --bun shadcn@latest add input
1258
+ bunx --bun shadcn@latest add textarea
1259
+ bunx --bun shadcn@latest add accordion
1260
+ bunx --bun shadcn@latest add dialog
1261
+ bunx --bun shadcn@latest add dropdown-menu
1262
+ bunx --bun shadcn@latest add toast
1263
+ bunx --bun shadcn@latest add tooltip
1264
+ ```
1265
+
1266
+ ### Inicializar Shadcn/UI (Primera vez)
1267
+
1268
+ ```bash
1269
+ # En el directorio del app (ej: apps/devenv-agent-ui)
1270
+ bunx --bun shadcn@latest init
1271
+
1272
+ # Responder las preguntas:
1273
+ # - Style: new-york (recomendado)
1274
+ # - Base color: neutral
1275
+ # - CSS variables: true
1276
+ # - Tailwind config: tailwind.config.ts
1277
+ # - Components path: @/components
1278
+ # - Utils path: @/lib/utils
1279
+ ```
1280
+
1281
+ ### Ejemplo Completo de Uso
1282
+
1283
+ ```typescript
1284
+ // components/agent/MessageItem.tsx
1285
+ import { AnimatedCard } from '@/components/ui/animated-card';
1286
+ import { MorphingText } from '@/components/ui/morphing-text';
1287
+ import { SlidingNumber } from '@/components/ui/sliding-number';
1288
+ import type { IMessage } from './MessageItem.types';
1289
+
1290
+ export function MessageItem({ message }: IMessageItemProps) {
1291
+ return (
1292
+ <AnimatedCard className="p-4">
1293
+ <div className="flex items-center gap-2">
1294
+ <MorphingText>{message.content}</MorphingText>
1295
+ {message.progress && (
1296
+ <SlidingNumber value={message.progress.percentage} />
1297
+ )}
1298
+ </div>
1299
+ </AnimatedCard>
1300
+ );
1301
+ }
1302
+ ```
1303
+
1304
+ ### MCP para Buscar Componentes
1305
+
1306
+ ```bash
1307
+ # Usar el MCP server de Shadcn para buscar componentes
1308
+ # Ver documentacion: https://ui.shadcn.com/docs/mcp
1309
+
1310
+ # Ejemplos de busqueda via MCP:
1311
+ # - "text animation components"
1312
+ # - "card with entrance animation"
1313
+ # - "progress with reveal effect"
1314
+ ```
1315
+
1316
+ ---
1317
+
1318
+ ## REGLA 14: Iconos Animados - lucie-animated
1319
+
1320
+ ### Stack de Iconos
1321
+
1322
+ **OBLIGATORIO**: lucie-animated como fuente principal para iconos animados.
1323
+
1324
+ ### Formato de Comandos
1325
+
1326
+ ```bash
1327
+ # CORRECTO - Formato estandar con Bun
1328
+ bunx --bun shadcn@latest add @lucie-animated/a-arrow-up
1329
+ bunx --bun shadcn@latest add @lucie-animated/arrow-down
1330
+ bunx --bun shadcn@latest add @lucie-animated/check-circle
1331
+ bunx --bun shadcn@latest add @lucie-animated/x-circle
1332
+ ```
1333
+
1334
+ ### Fuentes de Iconos
1335
+
1336
+ | Fuente | Prefijo | Tipo | Prioridad |
1337
+ |--------|---------|------|-----------|
1338
+ | **lucie-animated** | `@lucie-animated/*` | Iconos animados | **PREFERIDO** |
1339
+ | **lucide-react** | `lucide-react` | Iconos estándar sin animar | Alternativa |
1340
+ | **Radix Icons** | `@radix-ui/icons` | Iconos Radix | Solo si es necesario |
1341
+
1342
+ ### Catalogo Disponible
1343
+
1344
+ ```bash
1345
+ # Ver catalogo completo en:
1346
+ # https://lucie-animated.com/
1347
+
1348
+ # Iconos comunes disponibles:
1349
+ # - Flechas: a-arrow-up, a-arrow-down, a-arrow-left, a-arrow-right
1350
+ # - Acciones: check-circle, x-circle, plus-circle, minus-circle
1351
+ # - Navegacion: home, settings, user, bell, search
1352
+ # - Archivos: file, folder, download, upload
1353
+ # - Media: play, pause, volume, volume-x
1354
+ ```
1355
+
1356
+ ### Patrones de Importacion
1357
+
1358
+ ```typescript
1359
+ // CORRECTO - Importar desde components/icons
1360
+ import { AArrowUp } from '@/components/icons/a-arrow-up';
1361
+ import { ArrowDown } from '@/components/icons/arrow-down';
1362
+ import { CheckCircle } from '@/components/icons/check-circle';
1363
+ import { XCircle } from '@/components/icons/x-circle';
1364
+
1365
+ // CORRECTO - Icono estatico (cuando no existe version animada)
1366
+ import { Settings } from 'lucide-react';
1367
+
1368
+ // INCORRECTO - Importar directo desde libreria
1369
+ import { AArrowUp } from '@lucie-animated/a-arrow-up';
1370
+ ```
1371
+
1372
+ ### Estructura de Carpeta
1373
+
1374
+ ```
1375
+ components/
1376
+ └── icons/
1377
+ ├── a-arrow-up.tsx # Icono animado
1378
+ ├── arrow-down.tsx # Icono animado
1379
+ ├── check-circle.tsx # Icono animado
1380
+ └── index.ts # Barrel export
1381
+ ```
1382
+
1383
+ ### Ejemplo Completo de Uso
1384
+
1385
+ ```typescript
1386
+ // components/agent/AgentControls.tsx
1387
+ import { AArrowUp } from '@/components/icons/a-arrow-up';
1388
+ import { Play } from '@/components/icons/play';
1389
+ import { CheckCircle } from '@/components/icons/check-circle';
1390
+ import type { IAgentControlsProps } from './AgentControls.types';
1391
+
1392
+ export function AgentControls({ isRunning, onExecute }: IAgentControlsProps) {
1393
+ return (
1394
+ <div className="flex gap-2">
1395
+ <button onClick={onExecute}>
1396
+ {isRunning ? (
1397
+ <CheckCircle className="w-5 h-5" />
1398
+ ) : (
1399
+ <Play className="w-5 h-5" />
1400
+ )}
1401
+ </button>
1402
+ <AArrowUp className="w-5 h-5 animate-bounce" />
1403
+ </div>
1404
+ );
1405
+ }
1406
+ ```
1407
+
1408
+ ### Referencias
1409
+
1410
+ - **lucie-animated Gallery**: https://lucie-animated.com/
1411
+ - **lucide-react Documentation**: https://lucide.dev/
1412
+ - **Shadcn Icons**: https://ui.shadcn.com/docs/components/icon
1413
+
1414
+ ### Referencias
1415
+
1416
+ - **Shadcn/UI Documentation**: https://ui.shadcn.com/
1417
+ - **Shadcn MCP Registry**: https://ui.shadcn.com/docs/mcp
1418
+ - **Animate UI Gallery**: https://animate.ui/
1419
+ - **Radix UI Primitives**: https://www.radix-ui.com/
1420
+ - **CLI Documentation**: https://ui.shadcn.com/docs/cli
1421
+
1422
+ ---
1423
+
1424
+ ## REGLA 15: Testing - Vitest (NO bun test)
1425
+
1426
+ ### Runner Correcto
1427
+
1428
+ **OBLIGATORIO**: Usar `vitest` o `bunx vitest` para ejecutar tests. NUNCA `bun test`.
1429
+
1430
+ ```bash
1431
+ # CORRECTO - Vitest runner
1432
+ vitest # Modo watch
1433
+ vitest run # Single run
1434
+ bunx vitest run # Con bunx
1435
+ bunx vitest run src/hooks/ # Tests específicos
1436
+
1437
+ # INCORRECTO - Bun test runner
1438
+ bun test # NO USAR
1439
+ bun test src/ # NO USAR
1440
+ ```
1441
+
1442
+ ### Configuración Base
1443
+
1444
+ ```typescript
1445
+ // vitest.config.ts
1446
+ import { defineConfig } from 'vitest/config';
1447
+ import react from '@vitejs/plugin-react';
1448
+ import tsconfigPaths from 'vite-tsconfig-paths';
1449
+
1450
+ export default defineConfig({
1451
+ plugins: [react(), tsconfigPaths()],
1452
+ test: {
1453
+ environment: 'jsdom',
1454
+ setupFiles: ['./src/tests/setupTests.ts'],
1455
+ globals: true,
1456
+ },
1457
+ });
1458
+ ```
1459
+
1460
+ ### Setup con MSW
1461
+
1462
+ ```typescript
1463
+ // src/tests/setupTests.ts
1464
+ import '@testing-library/jest-dom';
1465
+ import { beforeAll, afterEach, afterAll } from 'vitest';
1466
+ import { server } from './mocks/server';
1467
+
1468
+ beforeAll(() => server.listen());
1469
+ afterEach(() => {
1470
+ server.resetHandlers();
1471
+ localStorage.clear();
1472
+ });
1473
+ afterAll(() => server.close());
1474
+ ```
1475
+
1476
+ ### Documentación Completa
1477
+
1478
+ Ver documentación detallada en:
1479
+ - `~/dotfiles/mks-ui/docs/testing-architecture.md`
1480
+ - `~/dotfiles/mks-ui/docs/testing-strategy.md`
1481
+
1482
+ ---
1483
+
1484
+ ## REGLA 16: Sistema de Themes - CSS Variables + Theme Manager
1485
+
1486
+ ### Stack de Theming
1487
+
1488
+ **OBLIGATORIO**: Usar sistema de CSS variables con tokens apropiados.
1489
+
1490
+ | Package | Uso | Instalación |
1491
+ |---------|-----|-------------|
1492
+ | **@mks2508/theme-manager-react** | Theme switching con animaciones | `bun add @mks2508/theme-manager-react` |
1493
+ | **@mks2508/shadcn-basecoat-theme-manager** | Core de gestión de temas | `bun add @mks2508/shadcn-basecoat-theme-manager` |
1494
+
1495
+ ### Variables CSS Requeridas
1496
+
1497
+ ```css
1498
+ :root {
1499
+ /* Colores base */
1500
+ --background: 0 0% 100%;
1501
+ --foreground: 222.2 84% 4.9%;
1502
+ --primary: 222.2 47.4% 11.2%;
1503
+ --primary-foreground: 210 40% 98%;
1504
+ --secondary: 210 40% 96%;
1505
+ --secondary-foreground: 222.2 84% 4.9%;
1506
+ --muted: 210 40% 96%;
1507
+ --muted-foreground: 215.4 16.3% 46.9%;
1508
+ --accent: 210 40% 96%;
1509
+ --accent-foreground: 222.2 84% 4.9%;
1510
+ --destructive: 0 84.2% 60.2%;
1511
+ --destructive-foreground: 210 40% 98%;
1512
+
1513
+ /* UI Elements */
1514
+ --card: var(--background);
1515
+ --card-foreground: var(--foreground);
1516
+ --popover: var(--background);
1517
+ --popover-foreground: var(--foreground);
1518
+ --border: 214.3 31.8% 91.4%;
1519
+ --input: 214.3 31.8% 91.4%;
1520
+ --ring: 222.2 84% 4.9%;
1521
+ --radius: 0.5rem;
1522
+ }
1523
+
1524
+ .dark {
1525
+ --background: 222.2 84% 4.9%;
1526
+ --foreground: 210 40% 98%;
1527
+ /* ... dark variants */
1528
+ }
1529
+ ```
1530
+
1531
+ ### Uso con Next.js
1532
+
1533
+ ```tsx
1534
+ // app/layout.tsx
1535
+ import { ThemeProvider } from '@mks2508/theme-manager-react/nextjs'
1536
+
1537
+ export default function RootLayout({ children }) {
1538
+ return (
1539
+ <html lang="en">
1540
+ <body>
1541
+ <ThemeProvider
1542
+ registryUrl="/themes/registry.json"
1543
+ defaultTheme="default"
1544
+ defaultMode="auto"
1545
+ enableTransitions={true}
1546
+ >
1547
+ {children}
1548
+ </ThemeProvider>
1549
+ </body>
1550
+ </html>
1551
+ )
1552
+ }
1553
+ ```
1554
+
1555
+ ### Componentes de Theme
1556
+
1557
+ ```tsx
1558
+ import {
1559
+ ModeToggle, // Toggle light/dark
1560
+ AnimatedThemeToggler, // Toggle con animaciones
1561
+ ThemeSelector // Selector completo de temas
1562
+ } from '@mks2508/theme-manager-react/nextjs'
1563
+ ```
1564
+
1565
+ ---
1566
+
1567
+ ## REGLA 17: Sidebar y Bottom Navigation - sidebar-headless
1568
+
1569
+ ### Package Oficial
1570
+
1571
+ **OBLIGATORIO**: Usar `@mks2508/sidebar-headless` para sidebars y bottom navigation.
1572
+
1573
+ ```bash
1574
+ bun add @mks2508/sidebar-headless
1575
+ ```
1576
+
1577
+ ### Características
1578
+
1579
+ - Headless sidebar y mobile bottom navigation
1580
+ - Animaciones fluidas
1581
+ - Keyboard navigation completa
1582
+ - WAI-ARIA accessibility
1583
+ - Soporte glassmorphism
1584
+ - Mobile-first design
1585
+
1586
+ ### Ejemplo de Uso
1587
+
1588
+ ```tsx
1589
+ import {
1590
+ Sidebar,
1591
+ SidebarItem,
1592
+ BottomNavigation
1593
+ } from '@mks2508/sidebar-headless';
1594
+
1595
+ export function AppLayout({ children }) {
1596
+ return (
1597
+ <div className="flex">
1598
+ {/* Desktop Sidebar */}
1599
+ <Sidebar className="hidden md:flex">
1600
+ <SidebarItem icon={<Home />} label="Home" href="/" />
1601
+ <SidebarItem icon={<Settings />} label="Settings" href="/settings" />
1602
+ </Sidebar>
1603
+
1604
+ {/* Mobile Bottom Nav */}
1605
+ <BottomNavigation className="md:hidden">
1606
+ <SidebarItem icon={<Home />} label="Home" href="/" />
1607
+ <SidebarItem icon={<Settings />} label="Settings" href="/settings" />
1608
+ </BottomNavigation>
1609
+
1610
+ <main>{children}</main>
1611
+ </div>
1612
+ );
1613
+ }
1614
+ ```
1615
+
1616
+ ---
1617
+
1618
+ ## REGLA 18: Glassmorphism - Guía de Implementación
1619
+
1620
+ ### Principios Fundamentales
1621
+
1622
+ Glassmorphism es la combinación controlada de:
1623
+ - **Transparencia parcial** (RGBA / alpha 10-30%)
1624
+ - **Backdrop blur** (difuminar lo que hay detrás)
1625
+ - **Bordes sutiles** (simular refracción)
1626
+ - **Sombras suaves** (profundidad)
1627
+ - **isolation: isolate** (stacking context)
1628
+
1629
+ ### Receta Base (Tailwind)
1630
+
1631
+ ```html
1632
+ <div class="
1633
+ isolate
1634
+ rounded-xl
1635
+ bg-white/20
1636
+ backdrop-blur-lg
1637
+ border border-white/30
1638
+ shadow-xl shadow-black/10
1639
+ ">
1640
+ </div>
1641
+ ```
1642
+
1643
+ ### Clases Glassmorphism Disponibles
1644
+
1645
+ ```css
1646
+ /* Paneles principales */
1647
+ .glass-panel /* blur-16px, borde primary */
1648
+ .glass-panel-subtle /* blur-8px, más transparente */
1649
+ .glass-panel-heavy /* blur-24px, más opaco */
1650
+
1651
+ /* Cards de producto */
1652
+ .product-card-glass /* blur-12px con hover */
1653
+ .product-card-glass-enhanced /* blur-16px con glow */
1654
+
1655
+ /* Badges */
1656
+ .badge-glass-featured /* Primary con glow */
1657
+ .badge-glass-subtle /* Sutil, borde transparente */
1658
+ .badge-glass-outline /* Solo borde */
1659
+
1660
+ /* Inputs y formularios */
1661
+ .input-glass /* Blur sutil con focus glow */
1662
+
1663
+ /* Headers */
1664
+ .bg-glass-header /* Desktop header */
1665
+ .bg-glass-header-mobile /* Mobile header más transparente */
1666
+ ```
1667
+
1668
+ ### Reglas de Uso
1669
+
1670
+ | Usar en | Evitar en |
1671
+ |---------|-----------|
1672
+ | Headers flotantes | Tablas densas |
1673
+ | Modales | Formularios largos |
1674
+ | Sidebars | Texto extenso |
1675
+ | Cards destacadas | Listas largas |
1676
+
1677
+ ### Performance
1678
+
1679
+ ```css
1680
+ /* Fallback obligatorio */
1681
+ @supports not (backdrop-filter: blur(1px)) {
1682
+ .glass-panel { background: rgba(255,255,255,0.9); }
1683
+ }
1684
+
1685
+ /* Reduced motion */
1686
+ @media (prefers-reduced-motion: reduce) {
1687
+ .glass-panel { transition: none; }
1688
+ }
1689
+ ```
1690
+
1691
+ ### Documentación Completa
1692
+
1693
+ - `~/dotfiles/mks-ui/docs/glassmorphism-guide.md` - Guía teórica
1694
+ - `~/dotfiles/mks-ui/docs/glassmorphism.css` - Implementación CSS completa
1695
+
1696
+ ---
1697
+
262
1698
  ## Fuentes de Referencia
263
1699
 
264
1700
  - **CLAUDE.md** - Guia de arquitectura del monorepo
265
1701
  - **@mks2508/better-logger** - Documentacion del logger
266
1702
  - **@mks2508/no-throw** - Documentacion del Result pattern
1703
+ - **@mks2508/theme-manager-react** - Sistema de themes
1704
+ - **@mks2508/sidebar-headless** - Sidebar y bottom navigation
267
1705
  - **Arktype** - https://arktype.io/
268
1706
  - **Rolldown** - https://rollup.rs/
269
1707
  - **Oxlint** - https://oxlint.com/
1708
+ - **Zustand** - https://zustand-demo.pmnd.rs/
1709
+ - **Shadcn/UI** - https://ui.shadcn.com/
1710
+ - **Animate UI** - https://animate.ui/
1711
+ - **lucie-animated** - https://lucie-animated.com/
1712
+
1713
+ ### Documentación en Dotfiles
1714
+
1715
+ | Documento | Ubicación | Contenido |
1716
+ |-----------|-----------|-----------|
1717
+ | **Glassmorphism Guide** | `~/dotfiles/mks-ui/docs/glassmorphism-guide.md` | Teoría y best practices |
1718
+ | **Glassmorphism CSS** | `~/dotfiles/mks-ui/docs/glassmorphism.css` | Implementación completa |
1719
+ | **Testing Architecture** | `~/dotfiles/mks-ui/docs/testing-architecture.md` | Arquitectura de tests |
1720
+ | **Testing Strategy** | `~/dotfiles/mks-ui/docs/testing-strategy.md` | Estrategia de testing |
1721
+
1722
+ ### CLI mks-ui
1723
+
1724
+ Generador de componentes disponible globalmente:
1725
+
1726
+ ```bash
1727
+ mks-ui component Button --ui # Componente UI simple
1728
+ mks-ui component ProductCard --complex # BLO completo
1729
+ mks-ui service ProductService # Servicio Elysia
1730
+ mks-ui hook useProducts # Custom hook
1731
+ mks-ui type Product # Tipos TypeScript
1732
+ ```