webs-sdk 0.19.62 → 0.19.65

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 (106) hide show
  1. package/AGENTS.md +44 -0
  2. package/CLAUDE.md +80 -0
  3. package/README.md +63 -1
  4. package/dist/components/SpinningWheel/index.d.ts +1 -1
  5. package/dist/components/SpinningWheel/index.d.ts.map +1 -1
  6. package/dist/components/SpinningWheel/index.js +2 -1
  7. package/dist/components/SpinningWheel/index.js.map +1 -1
  8. package/dist/components/ui/ConfiguredMediaPlayer.d.ts +49 -0
  9. package/dist/components/ui/ConfiguredMediaPlayer.d.ts.map +1 -0
  10. package/dist/components/ui/ConfiguredMediaPlayer.js +111 -0
  11. package/dist/components/ui/ConfiguredMediaPlayer.js.map +1 -0
  12. package/dist/components/ui/badge.d.ts +1 -0
  13. package/dist/components/ui/badge.d.ts.map +1 -1
  14. package/dist/components/ui/badge.js +1 -1
  15. package/dist/components/ui/badge.js.map +1 -1
  16. package/dist/components/ui/compose-refs.d.ts +6 -0
  17. package/dist/components/ui/compose-refs.d.ts.map +1 -0
  18. package/dist/components/ui/compose-refs.js +57 -0
  19. package/dist/components/ui/compose-refs.js.map +1 -0
  20. package/dist/components/ui/dropdown-menu.d.ts +3 -1
  21. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  22. package/dist/components/ui/dropdown-menu.js +2 -2
  23. package/dist/components/ui/dropdown-menu.js.map +1 -1
  24. package/dist/components/ui/index.d.ts +2 -0
  25. package/dist/components/ui/index.d.ts.map +1 -1
  26. package/dist/components/ui/index.js +2 -0
  27. package/dist/components/ui/index.js.map +1 -1
  28. package/dist/components/ui/media-player.d.ts +127 -0
  29. package/dist/components/ui/media-player.d.ts.map +1 -0
  30. package/dist/components/ui/media-player.js +1633 -0
  31. package/dist/components/ui/media-player.js.map +1 -0
  32. package/dist/components/ui/tooltip.d.ts +3 -1
  33. package/dist/components/ui/tooltip.d.ts.map +1 -1
  34. package/dist/components/ui/tooltip.js +2 -2
  35. package/dist/components/ui/tooltip.js.map +1 -1
  36. package/dist/components/ui/use-lazy-ref.d.ts +4 -0
  37. package/dist/components/ui/use-lazy-ref.d.ts.map +1 -0
  38. package/dist/components/ui/use-lazy-ref.js +45 -0
  39. package/dist/components/ui/use-lazy-ref.js.map +1 -0
  40. package/dist/config.d.ts +3 -0
  41. package/dist/config.d.ts.map +1 -1
  42. package/dist/config.js +3 -0
  43. package/dist/config.js.map +1 -1
  44. package/dist/index.d.ts +3 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +6 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/libraries/auth.d.ts.map +1 -1
  49. package/dist/libraries/auth.js +2 -2
  50. package/dist/libraries/auth.js.map +1 -1
  51. package/dist/libraries/clarity.d.ts +23 -0
  52. package/dist/libraries/clarity.d.ts.map +1 -0
  53. package/dist/libraries/clarity.js +110 -0
  54. package/dist/libraries/clarity.js.map +1 -0
  55. package/dist/libraries/index.d.ts +1 -0
  56. package/dist/libraries/index.d.ts.map +1 -1
  57. package/dist/libraries/index.js +3 -1
  58. package/dist/libraries/index.js.map +1 -1
  59. package/dist/libraries/networking.d.ts +4 -0
  60. package/dist/libraries/networking.d.ts.map +1 -1
  61. package/dist/libraries/networking.js +42 -6
  62. package/dist/libraries/networking.js.map +1 -1
  63. package/dist/libraries/session.d.ts.map +1 -1
  64. package/dist/libraries/session.js +2 -0
  65. package/dist/libraries/session.js.map +1 -1
  66. package/dist/network.d.ts +12 -0
  67. package/dist/network.d.ts.map +1 -0
  68. package/dist/network.js +15 -0
  69. package/dist/network.js.map +1 -0
  70. package/dist/server.d.ts +54 -0
  71. package/dist/server.d.ts.map +1 -0
  72. package/dist/server.js +74 -0
  73. package/dist/server.js.map +1 -0
  74. package/dist/types/media-chrome-store-types.d.ts +107 -0
  75. package/dist/types/media-chrome-store-types.d.ts.map +1 -0
  76. package/dist/types/media-chrome-store-types.js +3 -0
  77. package/dist/types/media-chrome-store-types.js.map +1 -0
  78. package/dist/utils/QRCode.d.ts +1 -0
  79. package/dist/utils/QRCode.d.ts.map +1 -0
  80. package/dist/utils/QRCode.js +2 -0
  81. package/dist/utils/QRCode.js.map +1 -0
  82. package/dist/utils/imageValidation.d.ts +6 -0
  83. package/dist/utils/imageValidation.d.ts.map +1 -0
  84. package/dist/utils/imageValidation.js +64 -0
  85. package/dist/utils/imageValidation.js.map +1 -0
  86. package/docs/CONFIGURED_MEDIA_PLAYER.md +40 -0
  87. package/docs/CORP_AUTH_USAGE.md +687 -0
  88. package/docs/EXAMPLES_WALLPAPERSNAME_IMPLEMENTATION.md +1445 -0
  89. package/docs/EXAMPLE_ENDPOINTS_COMUNICATION.md +55 -0
  90. package/docs/EXTERNAL_SDK_GUIDE.md +1340 -0
  91. package/docs/FOR_YOU_360_FACESWAP_FIX.md +400 -0
  92. package/docs/FOR_YOU_IMPLEMENTATION_GUIDE.md +1413 -0
  93. package/docs/FOR_YOU_QUIZ_ARCHITECTURE.md +753 -0
  94. package/docs/FOR_YOU_SDK_WALLPAPERSNAME_BUG.md +106 -0
  95. package/docs/FOR_YOU_WALLPAPERSNAME_ENHANCEMENT_PLAN.md +210 -0
  96. package/docs/LEGAL_USAGE.md +190 -0
  97. package/docs/STREAK_IMPLEMENTATION_GUIDE.md +430 -0
  98. package/package.json +29 -6
  99. package/scripts/install-agent-skill.cjs +34 -0
  100. package/skills/webs-sdk-package/SKILL.md +11 -0
  101. package/skills/webs-sdk-package/references/components.md +30 -0
  102. package/skills/webs-sdk-package/references/integration.md +32 -0
  103. package/dist/libraries/globals.d.ts +0 -17
  104. package/dist/libraries/globals.d.ts.map +0 -1
  105. package/dist/libraries/globals.js +0 -38
  106. package/dist/libraries/globals.js.map +0 -1
@@ -0,0 +1,430 @@
1
+ # Streak — Guía de implementación
2
+
3
+ Módulo de racha de actividad del usuario. Muestra un calendario mensual/semanal con los días en que el usuario registró actividad, su racha actual, la mejor racha y una celebración cuando suma un nuevo día.
4
+
5
+ ---
6
+
7
+ ## Índice
8
+
9
+ 1. [Arquitectura](#1-arquitectura)
10
+ 2. [Requisito previo: proxy API](#2-requisito-previo-proxy-api)
11
+ 3. [Implementación mínima](#3-implementación-mínima)
12
+ 4. [Implementación completa con i18n y config remota](#4-implementación-completa-con-i18n-y-config-remota)
13
+ 5. [Página dedicada con fondo configurable](#5-página-dedicada-con-fondo-configurable)
14
+ 6. [Props de StreakWidget](#6-props-de-streakwidget)
15
+ 7. [StreakSdkConfig — configuración de negocio](#7-streaksdkconfig--configuración-de-negocio)
16
+ 8. [StreakTexts — i18n](#8-streaktexts--i18n)
17
+ 9. [StreakTheme — theming visual](#9-streaktheme--theming-visual)
18
+ 10. [CSS Custom Properties — theming sin props](#10-css-custom-properties--theming-sin-props)
19
+ 11. [Primitivos avanzados](#11-primitivos-avanzados)
20
+ 12. [Checklist de implementación](#12-checklist-de-implementación)
21
+
22
+ ---
23
+
24
+ ## 1. Arquitectura
25
+
26
+ ```
27
+ webs-sdk
28
+ └── StreakWidget ← punto de entrada recomendado
29
+ ├── useStreak ← fetch de datos (agnóstico de portal)
30
+ ├── StreakCard ← UI del calendario (mensual + semanal)
31
+ ├── StreakModal ← envuelve la card en un modal overlay
32
+ └── StreakCelebration ← popup de hito de racha
33
+
34
+ portal (ej. efcweb)
35
+ └── [Portal]StreakWidget ← thin wrapper
36
+ ├── lee sesión y websiteId del store del portal
37
+ ├── construye `texts` desde i18n del portal
38
+ └── renderiza <StreakWidget> del SDK con props planas
39
+ ```
40
+
41
+ **Regla de oro:** el portal solo aporta lo que el SDK no puede conocer — sesión, websiteId, router y textos i18n. Todo lo demás lo gestiona el SDK internamente.
42
+
43
+ ---
44
+
45
+ ## 2. Requisito previo: proxy API
46
+
47
+ El SDK **nunca llama directamente al backend**. El portal debe exponer un endpoint proxy. El SDK llamará a `apiBaseUrl` con `POST` y este body:
48
+
49
+ ```ts
50
+ { user_id: string; app_user_id: string; website_id: string; keywords: string[]; days: number; }
51
+ ```
52
+
53
+ Respuesta esperada:
54
+
55
+ ```ts
56
+ { success: 1, data: { "login": ["2026-08-01", "2026-08-02"] } }
57
+ ```
58
+
59
+ ### Ejemplo de proxy en Next.js
60
+
61
+ ```ts
62
+ // app/api/streak/route.ts
63
+ import { NextRequest, NextResponse } from 'next/server';
64
+
65
+ export async function POST(req: NextRequest) {
66
+ const body = await req.json();
67
+ const res = await fetch(`${process.env.BACKEND_URL}/streak/get`, {
68
+ method: 'POST',
69
+ headers: { 'Content-Type': 'application/json' },
70
+ body: JSON.stringify(body),
71
+ });
72
+ return NextResponse.json(await res.json());
73
+ }
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 3. Implementación mínima
79
+
80
+ Sin i18n ni config remota. Usa los textos en español por defecto del SDK.
81
+
82
+ ```tsx
83
+ import { StreakWidget } from 'webs-sdk';
84
+
85
+ function MyStreakPage({ userId, websiteId }: { userId: string; websiteId: string }) {
86
+ return <StreakWidget userId={userId} websiteId={websiteId} apiBaseUrl="/api/streak" />;
87
+ }
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 4. Implementación completa con i18n y config remota
93
+
94
+ Patrón recomendado para producción. El portal crea un thin wrapper que lee sesión, config remota y textos i18n, y los pasa como props planas al SDK.
95
+
96
+ ```tsx
97
+ // components/streak/PortalStreakWidget.tsx
98
+ 'use client';
99
+
100
+ import { StreakWidget } from 'webs-sdk';
101
+ import type { StreakSdkConfig, StreakTexts, StreakTheme } from 'webs-sdk';
102
+ import { useAuthStore } from '@/store/authStore'; // adaptar al portal
103
+ import { useWebsite } from '@/hooks/useWebsite'; // adaptar al portal
104
+ import { useWebsiteData } from '@/hooks/useWebsiteData'; // adaptar al portal
105
+ import { useRouter } from '@/i18n/navigation'; // adaptar al portal
106
+ import { useTranslations } from 'next-intl'; // adaptar al portal
107
+
108
+ export function PortalStreakWidget({ asModal = true, className, theme, activityIcon, config }) {
109
+ const { userSession, isAuthenticated, _hasHydrated } = useAuthStore();
110
+ const { websiteData } = useWebsite();
111
+ const { getWebsiteData } = useWebsiteData();
112
+ const router = useRouter();
113
+ const t = useTranslations('streak'); // adaptar namespace
114
+
115
+ // Config: prop explícita > product_config remoto > defaults del SDK
116
+ const remoteConfig: StreakSdkConfig | undefined = getWebsiteData('product_config.streak') || undefined;
117
+ const streakConfig = config ?? remoteConfig;
118
+
119
+ // Guard: no renderizar sin sesión o con feature inactivo
120
+ if (!_hasHydrated || !isAuthenticated || !userSession?.us_id || !websiteData?.web_id || streakConfig?.active === false) {
121
+ return null;
122
+ }
123
+
124
+ const texts: StreakTexts = {
125
+ title: t('title'),
126
+ subtitle: t('subtitle'),
127
+ loading: t('loading'),
128
+ errorMessage: t('errorMessage'),
129
+ currentStreakLabel: t('currentStreakLabel'),
130
+ bestStreakLabel: t('bestStreakLabel'),
131
+ streakCountLabel: t('streakCountLabel', { count: '{count}' }),
132
+ celebrationTitle: t('celebrationTitle'),
133
+ celebrationMessage: t('celebrationMessage'),
134
+ closeButton: t('closeButton'),
135
+ weekdayLabels: t('weekdayLabels').split(',').map((s) => s.trim()),
136
+ calendarLocale: t('calendarLocale'),
137
+ prevMonthLabel: t('prevMonthLabel'),
138
+ nextMonthLabel: t('nextMonthLabel'),
139
+ thisWeekLabel: t('thisWeekLabel'),
140
+ switchToWeeklyLabel: t('switchToWeeklyLabel'),
141
+ switchToMonthlyLabel: t('switchToMonthlyLabel'),
142
+ };
143
+
144
+ return (
145
+ <StreakWidget
146
+ userId={String(userSession.us_id)}
147
+ websiteId={websiteData.web_id}
148
+ apiBaseUrl="/api/streak"
149
+ config={streakConfig}
150
+ texts={texts}
151
+ theme={theme}
152
+ activityIcon={activityIcon}
153
+ asModal={asModal}
154
+ className={className}
155
+ onCtaClick={(action) => router.push(action)}
156
+ />
157
+ );
158
+ }
159
+ ```
160
+
161
+ ### Claves i18n necesarias
162
+
163
+ ```json
164
+ {
165
+ "streak": {
166
+ "title": "Tu racha",
167
+ "subtitle": "Consigue actividad todos los días para mantener tu racha",
168
+ "loading": "Cargando tu racha...",
169
+ "errorMessage": "No se pudo cargar tu racha. Inténtalo más tarde.",
170
+ "currentStreakLabel": "Racha actual",
171
+ "bestStreakLabel": "Mejor racha",
172
+ "streakCountLabel": "{count} días de racha (esta semana)",
173
+ "celebrationTitle": "¡Racha conseguida!",
174
+ "celebrationMessage": "Has sumado un nuevo día a tu racha. ¡Sigue así!",
175
+ "closeButton": "Cerrar",
176
+ "weekdayLabels": "Lu, Ma, Mi, Ju, Vi, Sa, Do",
177
+ "calendarLocale": "es-ES",
178
+ "prevMonthLabel": "Mes anterior",
179
+ "nextMonthLabel": "Mes siguiente",
180
+ "thisWeekLabel": "Esta semana",
181
+ "switchToWeeklyLabel": "Ver semanal",
182
+ "switchToMonthlyLabel": "Ver mensual"
183
+ }
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 5. Página dedicada con fondo configurable
190
+
191
+ ```tsx
192
+ // app/[locale]/my_account/streak/page.tsx
193
+ 'use client';
194
+
195
+ import { useEffect } from 'react';
196
+ import { useRouter } from '@/i18n/navigation';
197
+ import { useAuthStore } from '@/store/authStore';
198
+ import { useWebsiteData } from '@/hooks/useWebsiteData';
199
+ import { resolveStreakPageBackground, type StreakSdkConfig } from 'webs-sdk';
200
+ import { PortalStreakWidget } from '@/components/streak/PortalStreakWidget';
201
+
202
+ export default function StreakPage() {
203
+ const router = useRouter();
204
+ const { userSession, isAuthenticated, _hasHydrated } = useAuthStore();
205
+ const { getWebsiteData } = useWebsiteData();
206
+
207
+ useEffect(() => {
208
+ if (_hasHydrated && !isAuthenticated) router.push('/login?redirect=/my_account/streak');
209
+ }, [_hasHydrated, isAuthenticated, router]);
210
+
211
+ if (!_hasHydrated || !isAuthenticated || !userSession) return null;
212
+
213
+ // Adaptar la ruta según product_config del portal
214
+ const childs: StreakSdkConfig[] = getWebsiteData('product_config.my_account.childs') || [];
215
+ const streakConfig = childs.find((c) => c.key === 'streak');
216
+
217
+ if (!streakConfig || streakConfig.active === false) {
218
+ router.replace('/my_account');
219
+ return null;
220
+ }
221
+
222
+ const bgStyle = resolveStreakPageBackground(streakConfig.background);
223
+
224
+ return (
225
+ <div className="relative flex flex-col flex-1 w-full" style={{ background: 'var(--color-background, #000000)' }}>
226
+ <div className="absolute bottom-0 left-0 right-0 h-1/2" style={bgStyle} aria-hidden="true" />
227
+ <div className="relative z-10 flex flex-col flex-1 w-full">
228
+ <PortalStreakWidget asModal={false} config={streakConfig} />
229
+ </div>
230
+ </div>
231
+ );
232
+ }
233
+ ```
234
+
235
+ ### Valores de `config.background`
236
+
237
+ | Valor | Resultado |
238
+ |---|---|
239
+ | `undefined` / `""` | `--streak-page-bg` del CSS del tema (fallback: `--primary-500`) |
240
+ | `"none"` | `--color-background` del tema |
241
+ | `"#E4002B"` / `"rgb(...)"` | `background-color` con ese valor |
242
+ | `"https://..."` / `"/img/bg.jpg"` | `background-image: url(...)` con `cover` |
243
+
244
+ ---
245
+
246
+ ## 6. Props de StreakWidget
247
+
248
+ | Prop | Tipo | Req | Descripción |
249
+ |---|---|---|---|
250
+ | `userId` | `string` | ✅ | ID del usuario autenticado. Sin valor, no hay fetch. |
251
+ | `websiteId` | `string` | ✅ | ID del portal. |
252
+ | `apiBaseUrl` | `string` | ✅ | Ruta del proxy. Ej: `"/api/streak"`. |
253
+ | `config` | `StreakSdkConfig` | — | Configuración de negocio. |
254
+ | `texts` | `StreakTexts` | — | Textos i18n. Default: español. |
255
+ | `theme` | `StreakTheme` | — | Overrides de color. |
256
+ | `activityIcon` | `string` | — | URL de imagen para días completados. |
257
+ | `asModal` | `boolean` | — | `true` = modal overlay. Default: `false`. |
258
+ | `className` | `string` | — | Clase extra sobre el contenedor raíz. |
259
+ ---
260
+
261
+ ## 7. StreakSdkConfig — configuración de negocio
262
+
263
+ Viene de `product_config` del portal. Todos los campos son opcionales.
264
+
265
+ ```ts
266
+ {
267
+ key?: string; // identificador, ej. "streak"
268
+ active?: boolean; // false → no renderizar. Default: true
269
+ period?: 'monthly' | 'weekly' | 'both'; // Default: "both"
270
+ defaultView?: 'monthly' | 'weekly'; // Default: "monthly"
271
+ icon?: string; // URL de imagen para días completados
272
+ keywords?: string[]; // actividades a consultar. Default: ["login"]
273
+ days?: number; // rango de días a pedir. Default: 30
274
+ is_navigable?: boolean; // muestra botones ‹ ›. Default: true
275
+ theme?: string; // nombre del theme CSS → clase "streak-theme-{nombre}"
276
+ background?: string; // fondo del área decorativa
277
+ custom_btn?: {
278
+ active?: boolean; // muestra CTA personalizado bajo la card
279
+ action?: string; // URL que se recibe en onCtaClick
280
+ };
281
+ celebration?: {
282
+ active?: boolean; // Default: true
283
+ icon?: string; // icono del popup (fallback: config.icon)
284
+ frequency?: number; // cada cuántos días celebrar. Default: 1
285
+ };
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 8. StreakTexts — i18n
292
+
293
+ Todos opcionales. Los no definidos usan el default en español del SDK.
294
+
295
+ ```ts
296
+ {
297
+ title?: string;
298
+ subtitle?: string;
299
+ loading?: string;
300
+ errorMessage?: string;
301
+ currentStreakLabel?: string;
302
+ bestStreakLabel?: string;
303
+ streakCountLabel?: string; // usar {count} como placeholder
304
+ celebrationTitle?: string;
305
+ celebrationMessage?: string;
306
+ closeButton?: string;
307
+ weekdayLabels?: string[]; // ['Lu','Ma','Mi','Ju','Vi','Sa','Do']
308
+ calendarLocale?: string; // BCP-47: "es-ES", "en-GB", "fr-FR"...
309
+ prevMonthLabel?: string;
310
+ nextMonthLabel?: string;
311
+ thisWeekLabel?: string;
312
+ switchToWeeklyLabel?: string;
313
+ switchToMonthlyLabel?: string;
314
+ }
315
+ ```
316
+
317
+ ---
318
+
319
+ ## 9. StreakTheme — theming visual
320
+
321
+ Solo las claves definidas generan una CSS custom property inline.
322
+
323
+ ```ts
324
+ {
325
+ primaryColor?: string; // bordes completados, CTA, dot de hoy
326
+ cardBackgroundColor?: string; // fondo del panel del calendario
327
+ backgroundTopColor?: string; // mitad superior del fondo decorativo
328
+ backgroundBottomColor?: string; // mitad inferior del fondo decorativo
329
+ pillColor?: string; // pill de la semana en curso
330
+ emptyBorderColor?: string; // borde días vacíos del mes
331
+ outOfMonthBorderColor?: string; // borde días de meses adyacentes
332
+ todayDotColor?: string; // punto bajo el día de hoy
333
+ textColor?: string; // texto principal en el panel
334
+ mutedTextColor?: string; // texto secundario en el panel
335
+ borderColor?: string; // borde del modal y del panel
336
+ heroTextColor?: string; // título/subtítulo fuera del panel
337
+ heroMutedTextColor?: string; // subtítulo secundario fuera del panel
338
+ ctaTextColor?: string; // texto del botón CTA
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ## 10. CSS Custom Properties — theming sin props
345
+
346
+ Para themes completos del portal, definir las variables en CSS sin tocar props.
347
+
348
+ ```css
349
+ /* Opción A: apuntar al componente directamente */
350
+ .streak-card {
351
+ --streak-primary-color: #E4002B;
352
+ --streak-card-bg: #FFFFFF;
353
+ --streak-bg-top: #FFFFFF;
354
+ --streak-bg-bottom: #FFDCDC;
355
+ --streak-pill-bg: #FFDCDC;
356
+ --streak-empty-border: #B5B5B5;
357
+ --streak-out-of-month-border: #E8E8E8;
358
+ --streak-today-dot: #E4002B;
359
+ --streak-text-color: #2B2B2B;
360
+ --streak-muted-text-color: #6D6665;
361
+ --streak-border-color: #E5E5E5;
362
+ --streak-hero-text-color: #2B2B2B;
363
+ --streak-hero-muted-text-color: #6D6665;
364
+ --streak-cta-text-color: #FFFFFF;
365
+ }
366
+ ```
367
+
368
+ ```css
369
+ /* Opción B: config.theme = "my-portal" → añade clase "streak-theme-my-portal" */
370
+ .streak-theme-my-portal {
371
+ --streak-primary-color: #E4002B;
372
+ /* ... resto de variables ... */
373
+ }
374
+ ```
375
+
376
+ ---
377
+
378
+ ## 11. Primitivos avanzados
379
+
380
+ Solo si `StreakWidget` no cubre el caso específico:
381
+
382
+ ```tsx
383
+ import {
384
+ StreakCard, // solo el calendario (sin fetch, sin modal, sin celebración)
385
+ StreakModal, // modal genérico que envuelve cualquier contenido
386
+ StreakCelebration, // popup de celebración independiente
387
+ useStreak, // hook de fetch puro
388
+ } from 'webs-sdk';
389
+ ```
390
+
391
+ > Usar primitivos solo si hay una razón específica. Para el 99% de los casos `StreakWidget` es suficiente.
392
+
393
+ ---
394
+
395
+ ## 12. Checklist de implementación
396
+
397
+ ### Backend / Proxy
398
+ - [ ] Endpoint proxy creado en el portal (ej. `POST /api/streak`)
399
+ - [ ] El proxy forwardea el body tal cual al backend real
400
+ - [ ] La respuesta tiene la forma `{ success: 1, data: { "login": ["YYYY-MM-DD", ...] } }`
401
+
402
+ ### SDK
403
+ - [ ] `webs-sdk` instalado y actualizado a la versión que incluye `StreakWidget`
404
+ - [ ] `StreakWidget` importado desde `webs-sdk`
405
+
406
+ ### Thin wrapper del portal
407
+ - [ ] Creado `[Portal]StreakWidget` que lee sesión, websiteId y config
408
+ - [ ] Guard implementado: no renderiza sin sesión o con `active === false`
409
+ - [ ] `texts` construido desde el sistema i18n del portal
410
+ - [ ] `onCtaClick` conectado al router del portal
411
+
412
+ ### i18n
413
+ - [ ] Todas las claves de `StreakTexts` añadidas a los archivos de traducción de cada idioma
414
+ - [ ] `weekdayLabels` como string separado por comas (`"Lu, Ma, Mi, ..."`)
415
+ - [ ] `calendarLocale` con el código BCP-47 correcto para cada idioma
416
+
417
+ ### Página dedicada (si aplica)
418
+ - [ ] Ruta creada con guard de autenticación y redirect a `/login`
419
+ - [ ] Guard de feature con redirect si `active === false`
420
+ - [ ] `resolveStreakPageBackground` importado de `webs-sdk` y usado para el fondo
421
+
422
+ ### Visual
423
+ - [ ] Theming aplicado (prop `theme`, CSS variables o `config.theme`)
424
+ - [ ] `activityIcon` configurado si el portal tiene imagen de actividad propia
425
+ - [ ] Validar skeleton de carga en el primer fetch
426
+ - [ ] Validar overlay sutil al navegar entre meses
427
+ - [ ] Validar popup de celebración al sumar un nuevo día de racha
428
+
429
+
430
+
package/package.json CHANGED
@@ -1,10 +1,16 @@
1
1
  {
2
2
  "name": "webs-sdk",
3
- "version": "0.19.62",
3
+ "version": "0.19.65",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
+ "pi": {
9
+ "skills": ["./skills/webs-sdk-package"]
10
+ },
11
+ "bin": {
12
+ "webs-sdk-skill": "./scripts/install-agent-skill.cjs"
13
+ },
8
14
  "exports": {
9
15
  ".": {
10
16
  "types": "./dist/index.d.ts",
@@ -21,7 +27,12 @@
21
27
  },
22
28
  "files": [
23
29
  "dist/**/*",
24
- "public/**/*"
30
+ "public/**/*",
31
+ "AGENTS.md",
32
+ "CLAUDE.md",
33
+ "docs/**/*",
34
+ "skills/**/*",
35
+ "scripts/install-agent-skill.cjs"
25
36
  ],
26
37
  "scripts": {
27
38
  "dev": "next dev --turbopack",
@@ -29,9 +40,15 @@
29
40
  "build:watch": "tsc --project tsconfig.build.json --watch",
30
41
  "prepublishOnly": "npm run build",
31
42
  "start": "next start",
32
- "lint": "next lint"
43
+ "lint": "eslint .",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "test:coverage": "vitest run --coverage",
47
+ "test:types": "tsc --project tsconfig.tests.json",
48
+ "test:package": "npm run build && node scripts/check-package.cjs"
33
49
  },
34
50
  "dependencies": {
51
+ "@microsoft/clarity": "^1.0.2",
35
52
  "@radix-ui/react-accordion": "^1.2.12",
36
53
  "@radix-ui/react-alert-dialog": "^1.1.15",
37
54
  "@radix-ui/react-aspect-ratio": "^1.1.7",
@@ -46,6 +63,7 @@
46
63
  "@radix-ui/react-navigation-menu": "^1.2.14",
47
64
  "@radix-ui/react-select": "^2.2.6",
48
65
  "@radix-ui/react-separator": "^1.1.7",
66
+ "@radix-ui/react-slider": "^1.4.7",
49
67
  "@radix-ui/react-slot": "^1.2.4",
50
68
  "@radix-ui/react-tabs": "^1.1.13",
51
69
  "@radix-ui/react-tooltip": "^1.2.8",
@@ -54,6 +72,7 @@
54
72
  "dompurify": "^3.4.12",
55
73
  "embla-carousel-react": "^8.3.2",
56
74
  "hls.js": "^1.6.16",
75
+ "media-chrome": "^4.19.3",
57
76
  "mixpanel-browser": "^2.72.0",
58
77
  "motion": "^12.38.0",
59
78
  "qrcode": "^1.5.4",
@@ -66,18 +85,22 @@
66
85
  "react-dom": "^18.0.0 || ^19.0.0"
67
86
  },
68
87
  "devDependencies": {
69
- "@eslint/eslintrc": "^3",
88
+ "@testing-library/react": "^16.3.3",
89
+ "@testing-library/user-event": "^14.6.7",
70
90
  "@types/dompurify": "^3.0.5",
71
91
  "@types/node": "^24.3.0",
72
92
  "@types/qrcode": "^1.5.6",
73
93
  "@types/react": "^19.2.14",
74
94
  "@types/react-dom": "^19.2.3",
95
+ "@vitest/coverage-v8": "^4.1.11",
75
96
  "eslint": "^9",
76
- "eslint-config-next": "^15.5.2",
97
+ "eslint-config-next": "^16.3.7",
98
+ "jsdom": "^26.1.0",
77
99
  "lucide-react": "^1.14.0",
78
100
  "next": "^16.0.8",
79
101
  "react": "^19.0.0",
80
102
  "react-dom": "^19.0.0",
81
- "typescript": "^5.9.3"
103
+ "typescript": "^5.9.3",
104
+ "vitest": "^4.1.11"
82
105
  }
83
106
  }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Copy the portable Agent Skills bundle into a consuming project.
5
+ // JavaScript is used because published CLI entry points must run on Node without TS tooling.
6
+ const fs = require('node:fs');
7
+ const path = require('node:path');
8
+
9
+ const args = process.argv.slice(2);
10
+ if (args.includes('--help') || args.includes('-h')) {
11
+ console.log('Usage: webs-sdk-skill [--force]\nInstall skill into .agents/skills/webs-sdk-package in the current directory.');
12
+ process.exit(0);
13
+ }
14
+ if (args.some((arg) => arg !== '--force')) {
15
+ console.error('Unknown option. Use webs-sdk-skill --help.');
16
+ process.exit(2);
17
+ }
18
+
19
+ const source = path.resolve(__dirname, '..', 'skills', 'webs-sdk-package');
20
+ const target = path.resolve(process.cwd(), '.agents', 'skills', 'webs-sdk-package');
21
+ if (!fs.existsSync(path.join(source, 'SKILL.md'))) {
22
+ console.error('Package is missing its agent skill.');
23
+ process.exit(1);
24
+ }
25
+ if (fs.existsSync(target)) {
26
+ if (!args.includes('--force')) {
27
+ console.error(`Skill already exists at ${target}. Run webs-sdk-skill --force to replace it.`);
28
+ process.exit(1);
29
+ }
30
+ fs.rmSync(target, { recursive: true, force: true });
31
+ }
32
+ fs.mkdirSync(path.dirname(target), { recursive: true });
33
+ fs.cpSync(source, target, { recursive: true });
34
+ console.log(`Installed webs-sdk skill at ${target}`);
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: webs-sdk-package
3
+ description: Use the webs-sdk npm package in Nexum portals. Activate when choosing or integrating reusable SDK functionality: authentication, sessions, tracking, consent, AI content components, For You, Streak, quiz, esports, Radix UI, VideoPlayer, ConfiguredMediaPlayer, MediaPlayer, or when checking whether the installed package already exports a needed component.
4
+ ---
5
+
6
+ # Use webs-sdk in a portal
7
+
8
+ 1. Read `references/components.md` to choose the existing component or library and its public import path. Read `references/integration.md` for the relevant usage and installation details.
9
+ 2. Verify the installed package version and exact public exports/types in `node_modules/webs-sdk/dist/index.d.ts` or `node_modules/webs-sdk/dist/components/ui/index.d.ts`. Catalog is a guide, not an exhaustive or version-independent API contract.
10
+ 3. Use `webs-sdk` or `webs-sdk/ui`; never import from private `dist/` paths. Keep default `WebsSDK` singleton out of client components. Add client boundary for interactive React UI. Consult installed `node_modules/webs-sdk/docs/` for feature-specific guidance.
11
+ 4. If functionality is absent, report the gap before creating shared functionality in a portal. Add reusable code to the SDK instead.
@@ -0,0 +1,30 @@
1
+ # Component and capability map
2
+
3
+ Choose an existing export before writing portal-specific code. Names below reflect the SDK source at packaging time; the installed package's `.d.ts` files are authoritative. Components accept different Props, API adapters and theme options: inspect their declarations rather than guessing required fields.
4
+
5
+ ## Root entry: `webs-sdk`
6
+
7
+ | Goal | Exports to inspect | Where it fits |
8
+ | --- | --- | --- |
9
+ | Session, authentication, users, portal metadata | Default `WebsSDK` singleton: `Session`, `AuthManager`, `User`, `Andromeda`, `ContentManager`, `CorporateAuthManager`, `Legal`, `Networking`, `Storage`, `Config`, `.pre` | SDK orchestration, typically server-side in Next.js API routes/middleware. Do not directly bundle default singleton in client UI. |
10
+ | Analytics and consent | `CookieConsent`, `getCookieConsentTexts`, `initGoogleAnalytics`, `updateGAConsent`, `trackGAEvent`, `trackGAPageView`, `isGALoaded`; singleton `MixPanel`, `Clarity`, `Calypso` | Cookie banner and opt-in GA; use consent APIs before loading analytics. |
11
+ | CDN and media checks | `handleCDNRequest`, `validateVideoUrl`, `validateVideoBatch`, `useValidateVideoOnMount`, `useLazyVideoValidation` | CDN route handler, video URL validation and hooks; not a video player. |
12
+ | Image/AI creation | `AvatarAI`, `CreativeFaceSwap`, `Wallpapers`, `WallpapersName`, `AETemplatesSection`, `MemeGenerator`, `Stickers`, `ARFilters`, `ArtGenerator`, `GifsIa`, `QrCode` | Standalone widgets and, where exported, `*Page` and `*ForYouCard` variants. Search Props for API/config/theme requirements. |
13
+ | Other content | `Ringtone`, `Quiz`, `EsportsNews`, `EsportsVideos`, `EsportsLive` | Content widgets, quizzes, esports with API adapter/config types. |
14
+ | For You page | `ForYouFullPage`, `ForYouPage`, `ForYouCard`, individual feature `*Card`, `useAvatarAI`, `useQuiz`, other `use*` hooks | Config-driven feature hub and reusable cards/hooks. Full page has config/theme/translation props; see installed `docs/FOR_YOU_IMPLEMENTATION_GUIDE.md`. |
15
+ | Streak | `StreakWidget`, `StreakCard`, `StreakModal`, `StreakCelebration`, `useStreak` | Activity streak UI and data hook; see installed `docs/STREAK_IMPLEMENTATION_GUIDE.md`. |
16
+ | Login support UI | `PasswordResetRequest`, `PasswordResetForm`, `RecoveryPassword` | Password recovery pieces; not a complete LoginPage. |
17
+ | Forms | `ContactForm`, `ContactFormContent`, `createContactFormAPI` | Reusable configurable contact form. |
18
+ | Networking/dev tools | `NetworkInspectorUI`, `trackRequest`, `trackResponse`, `getLogs`, `clearLogs`, `getStats` | Request inspection in development. |
19
+ | Small utilities | `downloadFile`, `generateQRCode`, `getForYouTranslations`, `forYouFetch` | Reuse instead of copy/pasting helpers. |
20
+
21
+ ## UI entry: `webs-sdk/ui`
22
+
23
+ | Goal | Export | Decision |
24
+ | --- | --- | --- |
25
+ | Prop-configured video/audio | `ConfiguredMediaPlayer` | Preferred new Radix player for a ready-to-use layout. Set `src`, optional `poster`, `tracks`, `sources`, `controls`; see `references/integration.md` and installed `docs/CONFIGURED_MEDIA_PLAYER.md`. |
26
+ | Custom media controls | `MediaPlayer` plus `MediaPlayerVideo` / `MediaPlayerAudio`, `MediaPlayerControls`, `MediaPlayerPlay`, `MediaPlayerSeek`, `MediaPlayerTime`, `MediaPlayerVolume`, `MediaPlayerSettings`, `MediaPlayerFullscreen`, etc. | Composable Radix parts; root alone does not create media or controls. |
27
+ | Existing/legacy video integration | `VideoPlayer` | Different API (`videoUrl`, `posterImage`, `languages`); retain for existing consumers. |
28
+ | Standard UI primitives | `Button`, `Input`, `Dialog`, `DropdownMenu`, `Tabs`, `Select`, `Tooltip`, `Avatar`, `Accordion`, `Alert`, `Badge`, `Carousel`, `Checkbox`, `NavigationMenu`, `Textarea`, etc. | Use exported Radix primitives; inspect the installed `dist/components/ui/index.d.ts` for full list and types. |
29
+
30
+ Both entry points have independent barrels. Import from `webs-sdk/ui` for UI, not `webs-sdk` or `webs-sdk/dist/...`. All React UI must run with a suitable client boundary and styling; consult `references/integration.md`.
@@ -0,0 +1,32 @@
1
+ # Install and use the SDK skill
2
+
3
+ ## Portal dependency and agent skill are separate
4
+
5
+ 1. Install the private npm package in the portal (requires registry access): `npm install webs-sdk`.
6
+ 2. To make the skill discoverable by Pi and other agents that support the Agent Skills directory convention, run from the portal root: `npm exec -- webs-sdk-skill`. This copies the skill to `.agents/skills/webs-sdk-package/`; commit that directory with the portal if teammates should get the skill. The installer never overwrites an existing installation unless called with `--force`. Restart the agent or reload skills. Installing npm package **alone does not activate** a skill in arbitrary agent tools.
7
+ 3. For Pi only, an alternative is `pi install npm:webs-sdk --local`: the package's `pi.skills` manifest enables its skill without copying files into the portal. This does **not** install `webs-sdk` as a dependency of the portal app: do step 1 as well. A personal install omits `--local`. Local Pi packages require project trust. Avoid enabling both paths simultaneously (duplicate skill names).
8
+ 4. Agent Skills-compatible tools can also load `skills/webs-sdk-package/SKILL.md` directly from the published package if configured to scan that directory. Other agents may require their own skill configuration.
9
+
10
+ When upgrading the npm package, rerun `npm exec -- webs-sdk-skill --force` if you copied the skill. The published skill version tracks the npm package, but the installed app dependency, copied skill, and agent's running context can be out of sync. Always check the installed declarations at `node_modules/webs-sdk/dist/index.d.ts` and `node_modules/webs-sdk/dist/components/ui/index.d.ts` before coding.
11
+
12
+ ## Usage patterns
13
+
14
+ ```tsx
15
+ 'use client';
16
+ import { ConfiguredMediaPlayer } from 'webs-sdk/ui';
17
+
18
+ export function Video({ src }: { src: string }) {
19
+ return <ConfiguredMediaPlayer src={src} controls={{ seekButtons: true, pictureInPicture: true }} />;
20
+ }
21
+ ```
22
+
23
+ For Tailwind CSS v4, the portal CSS must explicitly scan the installed SDK UI directory (normally ignored under `node_modules`): add `@source "../../node_modules/webs-sdk/dist/components/ui";` relative to the CSS file. Adjust depth to the portal layout; without generated utilities UI controls may appear unstyled. Consult `node_modules/webs-sdk/docs/CONFIGURED_MEDIA_PLAYER.md` for props, HLS and captions. `ConfiguredMediaPlayer` and `MediaPlayer` are different from legacy `VideoPlayer`.
24
+
25
+ ```ts
26
+ // In a server-side route/middleware, not a client component:
27
+ import WebsSDK from 'webs-sdk';
28
+
29
+ const metadata = await WebsSDK.Andromeda.getWebsiteMetadatabyHostname(hostname);
30
+ ```
31
+
32
+ Do not assume a backend response shape, API keys, or portal theme. Inspect the relevant exported Props and feature guide; fetch backend data through the existing server-side pattern. `webs-sdk/ui` is a client-side React entry and does not initialize sessions, authentication or analytics for you.
@@ -1,17 +0,0 @@
1
- declare global {
2
- interface Window {
3
- __WEBS_SDK_GLOBALS__?: {
4
- sessionID: string | null;
5
- };
6
- }
7
- }
8
- export declare const setGlobalSessionID: (sessionID: string | null) => void;
9
- export declare const getGlobalSessionID: () => string | null;
10
- export declare const clearGlobalSessionID: () => void;
11
- declare const _default: {
12
- setGlobalSessionID: (sessionID: string | null) => void;
13
- getGlobalSessionID: () => string | null;
14
- clearGlobalSessionID: () => void;
15
- };
16
- export default _default;
17
- //# sourceMappingURL=globals.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../../src/libraries/globals.ts"],"names":[],"mappings":"AAMA,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,oBAAoB,CAAC,EAAE;YACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;SAC5B,CAAC;KACL;CACJ;AAuBD,eAAO,MAAM,kBAAkB,GAAI,WAAW,MAAM,GAAG,IAAI,KAAG,IAI7D,CAAC;AAKF,eAAO,MAAM,kBAAkB,QAAO,MAAM,GAAG,IAI9C,CAAC;AAKF,eAAO,MAAM,oBAAoB,QAAO,IAIvC,CAAC;;oCAtB4C,MAAM,GAAG,IAAI,KAAG,IAAI;8BAS5B,MAAM,GAAG,IAAI;gCASX,IAAI;;AAM5C,wBAIE"}